Tests directories refractor
authorChocobozzz <me@florianbigard.com>
Thu, 28 Dec 2017 12:59:22 +0000 (13:59 +0100)
committerChocobozzz <me@florianbigard.com>
Thu, 28 Dec 2017 12:59:22 +0000 (13:59 +0100)
70 files changed:
server/tests/activitypub.ts
server/tests/api/check-params/services.ts
server/tests/api/check-params/video-comments.ts
server/tests/api/config.ts [deleted file]
server/tests/api/follows.ts [deleted file]
server/tests/api/index-fast.ts
server/tests/api/index-slow.ts
server/tests/api/jobs.ts [deleted file]
server/tests/api/multiple-servers.ts [deleted file]
server/tests/api/server/config.ts [new file with mode: 0644]
server/tests/api/server/follows.ts [new file with mode: 0644]
server/tests/api/server/jobs.ts [new file with mode: 0644]
server/tests/api/services.ts [deleted file]
server/tests/api/single-server.ts [deleted file]
server/tests/api/users.ts [deleted file]
server/tests/api/users/users.ts [new file with mode: 0644]
server/tests/api/video-abuse.ts [deleted file]
server/tests/api/video-blacklist-management.ts [deleted file]
server/tests/api/video-blacklist.ts [deleted file]
server/tests/api/video-channels.ts [deleted file]
server/tests/api/video-comments.ts [deleted file]
server/tests/api/video-description.ts [deleted file]
server/tests/api/video-privacy.ts [deleted file]
server/tests/api/video-transcoder.ts [deleted file]
server/tests/api/videos/multiple-servers.ts [new file with mode: 0644]
server/tests/api/videos/services.ts [new file with mode: 0644]
server/tests/api/videos/single-server.ts [new file with mode: 0644]
server/tests/api/videos/video-abuse.ts [new file with mode: 0644]
server/tests/api/videos/video-blacklist-management.ts [new file with mode: 0644]
server/tests/api/videos/video-blacklist.ts [new file with mode: 0644]
server/tests/api/videos/video-channels.ts [new file with mode: 0644]
server/tests/api/videos/video-comments.ts [new file with mode: 0644]
server/tests/api/videos/video-description.ts [new file with mode: 0644]
server/tests/api/videos/video-privacy.ts [new file with mode: 0644]
server/tests/api/videos/video-transcoder.ts [new file with mode: 0644]
server/tests/utils/activitypub.ts [deleted file]
server/tests/utils/cli.ts [deleted file]
server/tests/utils/cli/cli.ts [new file with mode: 0644]
server/tests/utils/clients.ts [deleted file]
server/tests/utils/config.ts [deleted file]
server/tests/utils/follows.ts [deleted file]
server/tests/utils/index.ts
server/tests/utils/jobs.ts [deleted file]
server/tests/utils/login.ts [deleted file]
server/tests/utils/miscs.ts [deleted file]
server/tests/utils/miscs/miscs.ts [new file with mode: 0644]
server/tests/utils/requests.ts [deleted file]
server/tests/utils/requests/requests.ts [new file with mode: 0644]
server/tests/utils/server/activitypub.ts [new file with mode: 0644]
server/tests/utils/server/clients.ts [new file with mode: 0644]
server/tests/utils/server/config.ts [new file with mode: 0644]
server/tests/utils/server/follows.ts [new file with mode: 0644]
server/tests/utils/server/jobs.ts [new file with mode: 0644]
server/tests/utils/server/servers.ts [new file with mode: 0644]
server/tests/utils/servers.ts [deleted file]
server/tests/utils/services.ts [deleted file]
server/tests/utils/users.ts [deleted file]
server/tests/utils/users/login.ts [new file with mode: 0644]
server/tests/utils/users/users.ts [new file with mode: 0644]
server/tests/utils/video-abuses.ts [deleted file]
server/tests/utils/video-blacklist.ts [deleted file]
server/tests/utils/video-channels.ts [deleted file]
server/tests/utils/video-comments.ts [deleted file]
server/tests/utils/videos.ts [deleted file]
server/tests/utils/videos/services.ts [new file with mode: 0644]
server/tests/utils/videos/video-abuses.ts [new file with mode: 0644]
server/tests/utils/videos/video-blacklist.ts [new file with mode: 0644]
server/tests/utils/videos/video-channels.ts [new file with mode: 0644]
server/tests/utils/videos/video-comments.ts [new file with mode: 0644]
server/tests/utils/videos/videos.ts [new file with mode: 0644]

index 333e02e8468d9cfa1c060f6f0282f57033256e74..94615c63f0895784f5e57e7e878bc1f0438978e1 100644 (file)
@@ -2,9 +2,7 @@
 
 import * as chai from 'chai'
 import 'mocha'
-import { flushTests, killallServers, ServerInfo, setAccessTokensToServers } from './utils'
-import { runServer } from './utils/servers'
-import { makeActivityPubGetRequest } from './utils/activitypub'
+import { flushTests, killallServers, makeActivityPubGetRequest, runServer, ServerInfo, setAccessTokensToServers } from './utils'
 
 const expect = chai.expect
 
index 780254df5ccc1f4d41b2f2849ca52da660f990a9..f82520574e588f9448c16ded2887cdf32712dcb1 100644 (file)
@@ -9,7 +9,7 @@ import {
   setAccessTokensToServers,
   killallServers
 } from '../../utils'
-import { getVideosList, uploadVideo } from '../../utils/videos'
+import { getVideosList, uploadVideo } from '../../utils/videos/videos'
 
 describe('Test services API validators', function () {
   let server
index e8d7ddf382362df6a384b1acd72fb4230c851f9d..f3832bd2cb4ece197903d4a80eb8623d062ea60c 100644 (file)
@@ -3,7 +3,7 @@
 import 'mocha'
 import * as request from 'supertest'
 import { flushTests, killallServers, makePostBodyRequest, runServer, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../utils'
-import { addVideoCommentThread } from '../../utils/video-comments'
+import { addVideoCommentThread } from '../../utils/videos/video-comments'
 
 describe('Test video comments API validator', function () {
   let pathThread: string
diff --git a/server/tests/api/config.ts b/server/tests/api/config.ts
deleted file mode 100644 (file)
index 61ae579..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import 'mocha'
-import * as chai from 'chai'
-const expect = chai.expect
-
-import {
-  getConfig,
-  flushTests,
-  runServer,
-  registerUser
-} from '../utils'
-
-describe('Test config', function () {
-  let server = null
-
-  before(async function () {
-    this.timeout(10000)
-
-    await flushTests()
-    server = await runServer(1)
-  })
-
-  it('Should have a correct config on a server with registration enabled', async function () {
-    const res = await getConfig(server.url)
-    const data = res.body
-
-    expect(data.signup.allowed).to.be.true
-  })
-
-  it('Should have a correct config on a server with registration enabled and a users limit', async function () {
-    this.timeout(5000)
-
-    await Promise.all([
-      registerUser(server.url, 'user1', 'super password'),
-      registerUser(server.url, 'user2', 'super password'),
-      registerUser(server.url, 'user3', 'super password')
-    ])
-
-    const res = await getConfig(server.url)
-    const data = res.body
-
-    expect(data.signup.allowed).to.be.false
-  })
-
-  after(async function () {
-    process.kill(-server.app.pid)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/follows.ts b/server/tests/api/follows.ts
deleted file mode 100644 (file)
index 2ffa426..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import { VideoComment, VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
-
-import {
-  flushAndRunMultipleServers, flushTests, getVideosList, killallServers, ServerInfo, setAccessTokensToServers, uploadVideo,
-  wait
-} from '../utils'
-import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../utils/follows'
-import { getUserAccessToken } from '../utils/login'
-import { dateIsValid, webtorrentAdd } from '../utils/miscs'
-import { createUser } from '../utils/users'
-import { addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads, getVideoThreadComments } from '../utils/video-comments'
-import { getVideo, rateVideo, testVideoImage } from '../utils/videos'
-
-const expect = chai.expect
-
-describe('Test follows', function () {
-  let servers: ServerInfo[] = []
-
-  before(async function () {
-    this.timeout(20000)
-
-    servers = await flushAndRunMultipleServers(3)
-
-    // Get the access tokens
-    await setAccessTokensToServers(servers)
-  })
-
-  it('Should not have followers', async function () {
-    for (const server of servers) {
-      const res = await getFollowersListPaginationAndSort(server.url, 0, 5, 'createdAt')
-      const follows = res.body.data
-
-      expect(res.body.total).to.equal(0)
-      expect(follows).to.be.an('array')
-      expect(follows.length).to.equal(0)
-    }
-  })
-
-  it('Should not have following', async function () {
-    for (const server of servers) {
-      const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
-      const follows = res.body.data
-
-      expect(res.body.total).to.equal(0)
-      expect(follows).to.be.an('array')
-      expect(follows.length).to.equal(0)
-    }
-  })
-
-  it('Should have server 1 following server 2 and 3', async function () {
-    this.timeout(10000)
-
-    await follow(servers[0].url, [ servers[1].url, servers[2].url ], servers[0].accessToken)
-
-    await wait(7000)
-  })
-
-  it('Should have 2 followings on server 1', async function () {
-    let res = await getFollowingListPaginationAndSort(servers[0].url, 0, 1, 'createdAt')
-    let follows = res.body.data
-
-    expect(res.body.total).to.equal(2)
-    expect(follows).to.be.an('array')
-    expect(follows.length).to.equal(1)
-
-    res = await getFollowingListPaginationAndSort(servers[0].url, 1, 1, 'createdAt')
-    follows = follows.concat(res.body.data)
-
-    const server2Follow = follows.find(f => f.following.host === 'localhost:9002')
-    const server3Follow = follows.find(f => f.following.host === 'localhost:9003')
-
-    expect(server2Follow).to.not.be.undefined
-    expect(server3Follow).to.not.be.undefined
-    expect(server2Follow.state).to.equal('accepted')
-    expect(server3Follow.state).to.equal('accepted')
-  })
-
-  it('Should have 0 followings on server 1 and 2', async function () {
-    for (const server of [ servers[1], servers[2] ]) {
-      const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
-      const follows = res.body.data
-
-      expect(res.body.total).to.equal(0)
-      expect(follows).to.be.an('array')
-      expect(follows.length).to.equal(0)
-    }
-  })
-
-  it('Should have 1 followers on server 2 and 3', async function () {
-    for (const server of [ servers[1], servers[2] ]) {
-      let res = await getFollowersListPaginationAndSort(server.url, 0, 1, 'createdAt')
-
-      let follows = res.body.data
-      expect(res.body.total).to.equal(1)
-      expect(follows).to.be.an('array')
-      expect(follows.length).to.equal(1)
-      expect(follows[0].follower.host).to.equal('localhost:9001')
-    }
-  })
-
-  it('Should have 0 followers on server 1', async function () {
-    const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
-    const follows = res.body.data
-
-    expect(res.body.total).to.equal(0)
-    expect(follows).to.be.an('array')
-    expect(follows.length).to.equal(0)
-  })
-
-  it('Should unfollow server 3 on server 1', async function () {
-    this.timeout(5000)
-
-    await unfollow(servers[0].url, servers[0].accessToken, servers[2])
-
-    await wait(3000)
-  })
-
-  it('Should not follow server 3 on server 1 anymore', async function () {
-    const res = await getFollowingListPaginationAndSort(servers[0].url, 0, 2, 'createdAt')
-    let follows = res.body.data
-
-    expect(res.body.total).to.equal(1)
-    expect(follows).to.be.an('array')
-    expect(follows.length).to.equal(1)
-
-    expect(follows[0].following.host).to.equal('localhost:9002')
-  })
-
-  it('Should not have server 1 as follower on server 3 anymore', async function () {
-    const res = await getFollowersListPaginationAndSort(servers[2].url, 0, 1, 'createdAt')
-
-    let follows = res.body.data
-    expect(res.body.total).to.equal(0)
-    expect(follows).to.be.an('array')
-    expect(follows.length).to.equal(0)
-  })
-
-  it('Should upload a video on server 2 ans 3 and propagate only the video of server 2', async function () {
-    this.timeout(10000)
-
-    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'server2' })
-    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3' })
-
-    await wait(5000)
-
-    let res = await getVideosList(servers[0].url)
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data[0].name).to.equal('server2')
-
-    res = await getVideosList(servers[1].url)
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data[0].name).to.equal('server2')
-
-    res = await getVideosList(servers[2].url)
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data[0].name).to.equal('server3')
-  })
-
-  it('Should propagate previous uploaded videos on a new following', async function () {
-    this.timeout(20000)
-
-    const video4Attributes = {
-      name: 'server3-4',
-      category: 2,
-      nsfw: true,
-      licence: 6,
-      tags: [ 'tag1', 'tag2', 'tag3' ]
-    }
-
-    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-2' })
-    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-3' })
-    await uploadVideo(servers[2].url, servers[2].accessToken, video4Attributes)
-    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-5' })
-    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-6' })
-
-    {
-      const user = { username: 'captain', password: 'password' }
-      await createUser(servers[2].url, servers[2].accessToken, user.username, user.password)
-      const userAccessToken = await getUserAccessToken(servers[2], user)
-
-      const resVideos = await getVideosList(servers[ 2 ].url)
-      const video4 = resVideos.body.data.find(v => v.name === 'server3-4')
-
-      {
-        await rateVideo(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, 'like')
-        await rateVideo(servers[ 2 ].url, userAccessToken, video4.id, 'dislike')
-      }
-
-      {
-        const text = 'my super first comment'
-        const res = await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, text)
-        const threadId = res.body.comment.id
-
-        const text1 = 'my super answer to thread 1'
-        const childCommentRes = await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text1)
-        const childCommentId = childCommentRes.body.comment.id
-
-        const text2 = 'my super answer to answer of thread 1'
-        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, childCommentId, text2)
-
-        const text3 = 'my second answer to thread 1'
-        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text3)
-      }
-    }
-
-    await wait(5000)
-
-    // Server 1 follows server 3
-    await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken)
-
-    await wait(7000)
-
-    let res = await getVideosList(servers[0].url)
-    expect(res.body.total).to.equal(7)
-
-    const video2 = res.body.data.find(v => v.name === 'server3-2')
-    const video4 = res.body.data.find(v => v.name === 'server3-4')
-    const video6 = res.body.data.find(v => v.name === 'server3-6')
-
-    expect(video2).to.not.be.undefined
-    expect(video4).to.not.be.undefined
-    expect(video6).to.not.be.undefined
-
-    const res2 = await getVideo(servers[0].url, video4.id)
-    const videoDetails = res2.body
-
-    expect(videoDetails.name).to.equal('server3-4')
-    expect(videoDetails.category).to.equal(2)
-    expect(videoDetails.categoryLabel).to.equal('Films')
-    expect(videoDetails.licence).to.equal(6)
-    expect(videoDetails.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
-    expect(videoDetails.language).to.equal(3)
-    expect(videoDetails.languageLabel).to.equal('Mandarin')
-    expect(videoDetails.nsfw).to.be.ok
-    expect(videoDetails.description).to.equal('my super description')
-    expect(videoDetails.serverHost).to.equal('localhost:9003')
-    expect(videoDetails.accountName).to.equal('root')
-    expect(videoDetails.likes).to.equal(1)
-    expect(videoDetails.dislikes).to.equal(1)
-    expect(videoDetails.isLocal).to.be.false
-    expect(videoDetails.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
-    expect(dateIsValid(videoDetails.createdAt)).to.be.true
-    expect(dateIsValid(videoDetails.updatedAt)).to.be.true
-    expect(videoDetails.files).to.have.lengthOf(1)
-
-    const file = videoDetails.files[0]
-    const magnetUri = file.magnetUri
-    expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.torrentUrl).to.equal(`${servers[2].url}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
-    expect(file.fileUrl).to.equal(`${servers[2].url}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`)
-    expect(file.resolution).to.equal(720)
-    expect(file.resolutionLabel).to.equal('720p')
-    expect(file.size).to.equal(218910)
-
-    const test = await testVideoImage(servers[2].url, 'video_short.webm', videoDetails.thumbnailPath)
-    expect(test).to.equal(true)
-
-    const torrent = await webtorrentAdd(magnetUri)
-    expect(torrent.files).to.be.an('array')
-    expect(torrent.files.length).to.equal(1)
-    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-
-    {
-      const res1 = await getVideoCommentThreads(servers[0].url, video4.id, 0, 5)
-
-      expect(res1.body.total).to.equal(1)
-      expect(res1.body.data).to.be.an('array')
-      expect(res1.body.data).to.have.lengthOf(1)
-
-      const comment: VideoComment = res1.body.data[0]
-      expect(comment.inReplyToCommentId).to.be.null
-      expect(comment.text).equal('my super first comment')
-      expect(comment.videoId).to.equal(video4.id)
-      expect(comment.id).to.equal(comment.threadId)
-      expect(comment.account.name).to.equal('root')
-      expect(comment.account.host).to.equal('localhost:9003')
-      expect(comment.totalReplies).to.equal(3)
-      expect(dateIsValid(comment.createdAt as string)).to.be.true
-      expect(dateIsValid(comment.updatedAt as string)).to.be.true
-
-      const threadId = comment.threadId
-
-      const res2 = await getVideoThreadComments(servers[0].url, video4.id, threadId)
-
-      const tree: VideoCommentThreadTree = res2.body
-      expect(tree.comment.text).equal('my super first comment')
-      expect(tree.children).to.have.lengthOf(2)
-
-      const firstChild = tree.children[0]
-      expect(firstChild.comment.text).to.equal('my super answer to thread 1')
-      expect(firstChild.children).to.have.lengthOf(1)
-
-      const childOfFirstChild = firstChild.children[0]
-      expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
-      expect(childOfFirstChild.children).to.have.lengthOf(0)
-
-      const secondChild = tree.children[1]
-      expect(secondChild.comment.text).to.equal('my second answer to thread 1')
-      expect(secondChild.children).to.have.lengthOf(0)
-    }
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
index 35b414383739c5c435c8cd1b9e7303542fd2e7d3..e591d0fd24081d9003e7bd2984c7f1a64a3fa678 100644 (file)
@@ -1,11 +1,11 @@
 // Order of the tests we want to execute
-import './config'
+import './server/config'
 import './check-params'
-import './users'
-import './single-server'
-import './video-abuse'
-import './video-blacklist'
-import './video-blacklist-management'
-import './video-description'
-import './video-privacy'
-import './services'
+import './users/users'
+import './videos/single-server'
+import './videos/video-abuse'
+import './videos/video-blacklist'
+import './videos/video-blacklist-management'
+import './videos/video-description'
+import './videos/video-privacy'
+import './videos/services'
index b525d6f01277878ef8285075761ef3a61ac58e63..23b6526c7de43fa130e11542161123b47a3b2f12 100644 (file)
@@ -1,7 +1,7 @@
 // Order of the tests we want to execute
 // import './multiple-servers'
-import './video-transcoder'
-import './multiple-servers'
-import './follows'
-import './jobs'
-import './video-comments'
+import './videos/video-transcoder'
+import './videos/multiple-servers'
+import './server/follows'
+import './server/jobs'
+import './videos/video-comments'
diff --git a/server/tests/api/jobs.ts b/server/tests/api/jobs.ts
deleted file mode 100644 (file)
index 4d9b613..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import { flushTests, killallServers, ServerInfo, setAccessTokensToServers, wait } from '../utils'
-import { doubleFollow } from '../utils/follows'
-import { getJobsList, getJobsListPaginationAndSort } from '../utils/jobs'
-import { flushAndRunMultipleServers } from '../utils/servers'
-import { uploadVideo } from '../utils/videos'
-import { dateIsValid } from '../utils/miscs'
-
-const expect = chai.expect
-
-describe('Test jobs', function () {
-  let servers: ServerInfo[]
-
-  before(async function () {
-    this.timeout(30000)
-
-    servers = await flushAndRunMultipleServers(2)
-
-    await setAccessTokensToServers(servers)
-
-    // Server 1 and server 2 follow each other
-    await doubleFollow(servers[0], servers[1])
-  })
-
-  it('Should create some jobs', async function () {
-    this.timeout(30000)
-
-    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' })
-    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' })
-
-    await wait(15000)
-  })
-
-  it('Should list jobs', async function () {
-    const res = await getJobsList(servers[1].url, servers[1].accessToken)
-    expect(res.body.total).to.be.above(2)
-    expect(res.body.data).to.have.length.above(2)
-  })
-
-  it('Should list jobs with sort and pagination', async function () {
-    const res = await getJobsListPaginationAndSort(servers[1].url, servers[1].accessToken, 4, 1, 'createdAt')
-    expect(res.body.total).to.be.above(2)
-    expect(res.body.data).to.have.lengthOf(1)
-
-    const job = res.body.data[0]
-    expect(job.state).to.equal('success')
-    expect(job.category).to.equal('transcoding')
-    expect(job.handlerName).to.have.length.above(3)
-    expect(dateIsValid(job.createdAt)).to.be.true
-    expect(dateIsValid(job.updatedAt)).to.be.true
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/multiple-servers.ts b/server/tests/api/multiple-servers.ts
deleted file mode 100644 (file)
index 06274d4..0000000
+++ /dev/null
@@ -1,858 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import { join } from 'path'
-import * as request from 'supertest'
-import { VideoComment, VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
-
-import {
-  addVideoChannel, dateIsValid, doubleFollow, flushAndRunMultipleServers, flushTests, getUserAccessToken, getVideo,
-  getVideoChannelsList, getVideosList, killallServers, rateVideo, removeVideo, ServerInfo, setAccessTokensToServers, testVideoImage,
-  updateVideo, uploadVideo, wait, webtorrentAdd
-} from '../utils'
-import { createUser } from '../utils/users'
-import { addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads, getVideoThreadComments } from '../utils/video-comments'
-import { viewVideo } from '../utils/videos'
-
-const expect = chai.expect
-
-describe('Test multiple servers', function () {
-  let servers: ServerInfo[] = []
-  const toRemove = []
-  let videoUUID = ''
-  let videoChannelId: number
-
-  before(async function () {
-    this.timeout(120000)
-
-    servers = await flushAndRunMultipleServers(3)
-
-    // Get the access tokens
-    await setAccessTokensToServers(servers)
-
-    const videoChannel = {
-      name: 'my channel',
-      description: 'super channel'
-    }
-    await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel)
-    const channelRes = await getVideoChannelsList(servers[0].url, 0, 1)
-    videoChannelId = channelRes.body.data[0].id
-
-    // Server 1 and server 2 follow each other
-    await doubleFollow(servers[0], servers[1])
-    // Server 1 and server 3 follow each other
-    await doubleFollow(servers[0], servers[2])
-    // Server 2 and server 3 follow each other
-    await doubleFollow(servers[1], servers[2])
-  })
-
-  it('Should not have videos for all servers', async function () {
-    for (const server of servers) {
-      const res = await getVideosList(server.url)
-      const videos = res.body.data
-      expect(videos).to.be.an('array')
-      expect(videos.length).to.equal(0)
-    }
-  })
-
-  describe('Should upload the video and propagate on each server', function () {
-    it('Should upload the video on server 1 and propagate on each server', async function () {
-      this.timeout(25000)
-
-      const videoAttributes = {
-        name: 'my super name for server 1',
-        category: 5,
-        licence: 4,
-        language: 9,
-        nsfw: true,
-        description: 'my super description for server 1',
-        tags: [ 'tag1p1', 'tag2p1' ],
-        channelId: videoChannelId,
-        fixture: 'video_short1.webm'
-      }
-      await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
-
-      await wait(10000)
-
-      // All servers should have this video
-      for (const server of servers) {
-        let baseMagnet = null
-
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-        expect(videos).to.be.an('array')
-        expect(videos.length).to.equal(1)
-        const video = videos[0]
-        expect(video.name).to.equal('my super name for server 1')
-        expect(video.category).to.equal(5)
-        expect(video.categoryLabel).to.equal('Sports')
-        expect(video.licence).to.equal(4)
-        expect(video.licenceLabel).to.equal('Attribution - Non Commercial')
-        expect(video.language).to.equal(9)
-        expect(video.languageLabel).to.equal('Japanese')
-        expect(video.nsfw).to.be.ok
-        expect(video.description).to.equal('my super description for server 1')
-        expect(video.serverHost).to.equal('localhost:9001')
-        expect(video.duration).to.equal(10)
-        expect(dateIsValid(video.createdAt)).to.be.true
-        expect(dateIsValid(video.updatedAt)).to.be.true
-        expect(video.accountName).to.equal('root')
-
-        const res2 = await getVideo(server.url, video.uuid)
-        const videoDetails = res2.body
-
-        expect(videoDetails.channel.name).to.equal('my channel')
-        expect(videoDetails.channel.description).to.equal('super channel')
-        expect(videoDetails.account.name).to.equal('root')
-        expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
-        expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
-        expect(videoDetails.files).to.have.lengthOf(1)
-        expect(videoDetails.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ])
-
-        const file = videoDetails.files[0]
-        const magnetUri = file.magnetUri
-        expect(file.magnetUri).to.have.lengthOf.above(2)
-        expect(file.torrentUrl).to
-          .equal(`http://${videoDetails.serverHost}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
-        expect(file.fileUrl).to.equal(`http://${videoDetails.serverHost}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`)
-        expect(file.resolution).to.equal(720)
-        expect(file.resolutionLabel).to.equal('720p')
-        expect(file.size).to.equal(572456)
-
-        if (server.url !== 'http://localhost:9001') {
-          expect(video.isLocal).to.be.false
-          expect(videoDetails.channel.isLocal).to.be.false
-        } else {
-          expect(video.isLocal).to.be.true
-          expect(videoDetails.channel.isLocal).to.be.true
-        }
-
-        // All servers should have the same magnet Uri
-        if (baseMagnet === null) {
-          baseMagnet = magnetUri
-        } else {
-          expect(baseMagnet).to.equal(magnetUri)
-        }
-
-        const test = await testVideoImage(server.url, 'video_short1.webm', video.thumbnailPath)
-        expect(test).to.equal(true)
-      }
-    })
-
-    it('Should upload the video on server 2 and propagate on each server', async function () {
-      this.timeout(50000)
-
-      const user = {
-        username: 'user1',
-        password: 'super_password'
-      }
-      await createUser(servers[1].url, servers[1].accessToken, user.username, user.password)
-      const userAccessToken = await getUserAccessToken(servers[1], user)
-
-      const videoAttributes = {
-        name: 'my super name for server 2',
-        category: 4,
-        licence: 3,
-        language: 11,
-        nsfw: true,
-        description: 'my super description for server 2',
-        tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
-        fixture: 'video_short2.webm'
-      }
-      await uploadVideo(servers[1].url, userAccessToken, videoAttributes)
-
-      // Transcoding
-      await wait(30000)
-
-      // All servers should have this video
-      for (const server of servers) {
-        let baseMagnet = {}
-
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-        expect(videos).to.be.an('array')
-        expect(videos.length).to.equal(2)
-        const video = videos[1]
-        expect(video.name).to.equal('my super name for server 2')
-        expect(video.category).to.equal(4)
-        expect(video.categoryLabel).to.equal('Art')
-        expect(video.licence).to.equal(3)
-        expect(video.licenceLabel).to.equal('Attribution - No Derivatives')
-        expect(video.language).to.equal(11)
-        expect(video.languageLabel).to.equal('German')
-        expect(video.nsfw).to.be.true
-        expect(video.description).to.equal('my super description for server 2')
-        expect(video.serverHost).to.equal('localhost:9002')
-        expect(video.duration).to.equal(5)
-        expect(dateIsValid(video.createdAt)).to.be.true
-        expect(dateIsValid(video.updatedAt)).to.be.true
-        expect(video.accountName).to.equal('user1')
-
-        if (server.url !== 'http://localhost:9002') {
-          expect(video.isLocal).to.be.false
-        } else {
-          expect(video.isLocal).to.be.true
-        }
-
-        const res2 = await getVideo(server.url, video.uuid)
-        const videoDetails = res2.body
-
-        expect(videoDetails.channel.name).to.equal('Default user1 channel')
-        expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
-        expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
-        expect(videoDetails.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ])
-
-        expect(videoDetails.files).to.have.lengthOf(4)
-
-        // Check common attributes
-        for (const file of videoDetails.files) {
-          expect(file.magnetUri).to.have.lengthOf.above(2)
-
-          // All servers should have the same magnet Uri
-          if (baseMagnet[file.resolution] === undefined) {
-            baseMagnet[file.resolution] = file.magnet
-          } else {
-            expect(baseMagnet[file.resolution]).to.equal(file.magnet)
-          }
-        }
-
-        const file240p = videoDetails.files.find(f => f.resolution === 240)
-        expect(file240p).not.to.be.undefined
-        expect(file240p.resolutionLabel).to.equal('240p')
-        expect(file240p.size).to.be.above(180000).and.below(200000)
-
-        const file360p = videoDetails.files.find(f => f.resolution === 360)
-        expect(file360p).not.to.be.undefined
-        expect(file360p.resolutionLabel).to.equal('360p')
-        expect(file360p.size).to.be.above(270000).and.below(290000)
-
-        const file480p = videoDetails.files.find(f => f.resolution === 480)
-        expect(file480p).not.to.be.undefined
-        expect(file480p.resolutionLabel).to.equal('480p')
-        expect(file480p.size).to.be.above(380000).and.below(400000)
-
-        const file720p = videoDetails.files.find(f => f.resolution === 720)
-        expect(file720p).not.to.be.undefined
-        expect(file720p.resolutionLabel).to.equal('720p')
-        expect(file720p.size).to.be.above(700000).and.below(7200000)
-
-        const test = await testVideoImage(server.url, 'video_short2.webm', videoDetails.thumbnailPath)
-        expect(test).to.equal(true)
-      }
-    })
-
-    it('Should upload two videos on server 3 and propagate on each server', async function () {
-      this.timeout(45000)
-
-      const videoAttributes1 = {
-        name: 'my super name for server 3',
-        category: 6,
-        licence: 5,
-        language: 11,
-        nsfw: true,
-        description: 'my super description for server 3',
-        tags: [ 'tag1p3' ],
-        fixture: 'video_short3.webm'
-      }
-      await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1)
-
-      const videoAttributes2 = {
-        name: 'my super name for server 3-2',
-        category: 7,
-        licence: 6,
-        language: 12,
-        nsfw: false,
-        description: 'my super description for server 3-2',
-        tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
-        fixture: 'video_short.webm'
-      }
-      await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes2)
-
-      await wait(10000)
-
-      let baseMagnet = null
-      // All servers should have this video
-      for (const server of servers) {
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-        expect(videos).to.be.an('array')
-        expect(videos.length).to.equal(4)
-
-        // We not sure about the order of the two last uploads
-        let video1 = null
-        let video2 = null
-        if (videos[2].name === 'my super name for server 3') {
-          video1 = videos[2]
-          video2 = videos[3]
-        } else {
-          video1 = videos[3]
-          video2 = videos[2]
-        }
-
-        expect(video1.name).to.equal('my super name for server 3')
-        expect(video1.category).to.equal(6)
-        expect(video1.categoryLabel).to.equal('Travels')
-        expect(video1.licence).to.equal(5)
-        expect(video1.licenceLabel).to.equal('Attribution - Non Commercial - Share Alike')
-        expect(video1.language).to.equal(11)
-        expect(video1.languageLabel).to.equal('German')
-        expect(video1.nsfw).to.be.ok
-        expect(video1.description).to.equal('my super description for server 3')
-        expect(video1.serverHost).to.equal('localhost:9003')
-        expect(video1.duration).to.equal(5)
-        expect(video1.accountName).to.equal('root')
-        expect(dateIsValid(video1.createdAt)).to.be.true
-        expect(dateIsValid(video1.updatedAt)).to.be.true
-
-        const res2 = await getVideo(server.url, video1.id)
-        const video1Details = res2.body
-        expect(video1Details.files).to.have.lengthOf(1)
-        expect(video1Details.tags).to.deep.equal([ 'tag1p3' ])
-
-        const file1 = video1Details.files[0]
-        expect(file1.magnetUri).to.have.lengthOf.above(2)
-        expect(file1.resolution).to.equal(720)
-        expect(file1.resolutionLabel).to.equal('720p')
-        expect(file1.size).to.equal(292677)
-
-        expect(video2.name).to.equal('my super name for server 3-2')
-        expect(video2.category).to.equal(7)
-        expect(video2.categoryLabel).to.equal('Gaming')
-        expect(video2.licence).to.equal(6)
-        expect(video2.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
-        expect(video2.language).to.equal(12)
-        expect(video2.languageLabel).to.equal('Korean')
-        expect(video2.nsfw).to.be.false
-        expect(video2.description).to.equal('my super description for server 3-2')
-        expect(video2.serverHost).to.equal('localhost:9003')
-        expect(video2.duration).to.equal(5)
-        expect(video2.accountName).to.equal('root')
-        expect(dateIsValid(video2.createdAt)).to.be.true
-        expect(dateIsValid(video2.updatedAt)).to.be.true
-
-        const res3 = await getVideo(server.url, video2.id)
-        const video2Details = res3.body
-        expect(video2Details.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
-
-        expect(video2Details.files).to.have.lengthOf(1)
-
-        const file2 = video2Details.files[0]
-        const magnetUri2 = file2.magnetUri
-        expect(file2.magnetUri).to.have.lengthOf.above(2)
-        expect(file2.resolution).to.equal(720)
-        expect(file2.resolutionLabel).to.equal('720p')
-        expect(file2.size).to.equal(218910)
-
-        if (server.url !== 'http://localhost:9003') {
-          expect(video1.isLocal).to.be.false
-          expect(video2.isLocal).to.be.false
-        } else {
-          expect(video1.isLocal).to.be.true
-          expect(video2.isLocal).to.be.true
-        }
-
-        // All servers should have the same magnet Uri
-        if (baseMagnet === null) {
-          baseMagnet = magnetUri2
-        } else {
-          expect(baseMagnet).to.equal(magnetUri2)
-        }
-
-        const test1 = await testVideoImage(server.url, 'video_short3.webm', video1.thumbnailPath)
-        expect(test1).to.equal(true)
-
-        const test2 = await testVideoImage(server.url, 'video_short.webm', video2.thumbnailPath)
-        expect(test2).to.equal(true)
-      }
-    })
-  })
-
-  describe('Should seed the uploaded video', function () {
-    it('Should add the file 1 by asking server 3', async function () {
-      this.timeout(10000)
-
-      const res = await getVideosList(servers[2].url)
-
-      const video = res.body.data[0]
-      toRemove.push(res.body.data[2])
-      toRemove.push(res.body.data[3])
-
-      const res2 = await getVideo(servers[2].url, video.id)
-      const videoDetails = res2.body
-
-      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
-      expect(torrent.files).to.be.an('array')
-      expect(torrent.files.length).to.equal(1)
-      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-    })
-
-    it('Should add the file 2 by asking server 1', async function () {
-      this.timeout(10000)
-
-      const res = await getVideosList(servers[0].url)
-
-      const video = res.body.data[1]
-      const res2 = await getVideo(servers[0].url, video.id)
-      const videoDetails = res2.body
-
-      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
-      expect(torrent.files).to.be.an('array')
-      expect(torrent.files.length).to.equal(1)
-      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-    })
-
-    it('Should add the file 3 by asking server 2', async function () {
-      this.timeout(10000)
-
-      const res = await getVideosList(servers[1].url)
-
-      const video = res.body.data[2]
-      const res2 = await getVideo(servers[1].url, video.id)
-      const videoDetails = res2.body
-
-      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
-      expect(torrent.files).to.be.an('array')
-      expect(torrent.files.length).to.equal(1)
-      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-    })
-
-    it('Should add the file 3-2 by asking server 1', async function () {
-      this.timeout(10000)
-
-      const res = await getVideosList(servers[0].url)
-
-      const video = res.body.data[3]
-      const res2 = await getVideo(servers[0].url, video.id)
-      const videoDetails = res2.body
-
-      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
-      expect(torrent.files).to.be.an('array')
-      expect(torrent.files.length).to.equal(1)
-      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-    })
-
-    it('Should add the file 2 in 360p by asking server 1', async function () {
-      this.timeout(10000)
-
-      const res = await getVideosList(servers[0].url)
-
-      const video = res.body.data.find(v => v.name === 'my super name for server 2')
-      const res2 = await getVideo(servers[0].url, video.id)
-      const videoDetails = res2.body
-
-      const file = videoDetails.files.find(f => f.resolution === 360)
-      expect(file).not.to.be.undefined
-
-      const torrent = await webtorrentAdd(file.magnetUri)
-      expect(torrent.files).to.be.an('array')
-      expect(torrent.files.length).to.equal(1)
-      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-    })
-  })
-
-  describe('Should update video views, likes and dislikes', function () {
-    let localVideosServer3 = []
-    let remoteVideosServer1 = []
-    let remoteVideosServer2 = []
-    let remoteVideosServer3 = []
-
-    before(async function () {
-      const res1 = await getVideosList(servers[0].url)
-      remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
-
-      const res2 = await getVideosList(servers[1].url)
-      remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
-
-      const res3 = await getVideosList(servers[2].url)
-      localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid)
-      remoteVideosServer3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
-    })
-
-    it('Should view multiple videos on owned servers', async function () {
-      this.timeout(10000)
-
-      const tasks: Promise<any>[] = []
-      tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
-      tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
-      tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
-      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
-
-      await Promise.all(tasks)
-
-      await wait(5000)
-
-      for (const server of servers) {
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-        const video0 = videos.find(v => v.uuid === localVideosServer3[0])
-        const video1 = videos.find(v => v.uuid === localVideosServer3[1])
-
-        expect(video0.views).to.equal(3)
-        expect(video1.views).to.equal(1)
-      }
-    })
-
-    it('Should view multiple videos on each servers', async function () {
-      this.timeout(15000)
-
-      const tasks: Promise<any>[] = []
-      tasks.push(viewVideo(servers[0].url, remoteVideosServer1[0]))
-      tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0]))
-      tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0]))
-      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[0]))
-      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
-      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
-      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
-      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
-      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
-      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
-
-      await Promise.all(tasks)
-
-      await wait(10000)
-
-      let baseVideos = null
-
-      for (const server of servers) {
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-
-        // Initialize base videos for future comparisons
-        if (baseVideos === null) {
-          baseVideos = videos
-          continue
-        }
-
-        for (const baseVideo of baseVideos) {
-          const sameVideo = videos.find(video => video.name === baseVideo.name)
-          expect(baseVideo.views).to.equal(sameVideo.views)
-        }
-      }
-    })
-
-    it('Should like and dislikes videos on different services', async function () {
-      this.timeout(20000)
-
-      const tasks: Promise<any>[] = []
-      tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
-      tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike'))
-      tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
-      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like'))
-      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike'))
-      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike'))
-      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like'))
-
-      await Promise.all(tasks)
-
-      await wait(10000)
-
-      let baseVideos = null
-      for (const server of servers) {
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-
-        // Initialize base videos for future comparisons
-        if (baseVideos === null) {
-          baseVideos = videos
-          continue
-        }
-
-        for (const baseVideo of baseVideos) {
-          const sameVideo = videos.find(video => video.name === baseVideo.name)
-          expect(baseVideo.likes).to.equal(sameVideo.likes)
-          expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
-        }
-      }
-    })
-  })
-
-  describe('Should manipulate these videos', function () {
-    it('Should update the video 3 by asking server 3', async function () {
-      this.timeout(10000)
-
-      const attributes = {
-        name: 'my super video updated',
-        category: 10,
-        licence: 7,
-        language: 13,
-        nsfw: true,
-        description: 'my super description updated',
-        tags: [ 'tag_up_1', 'tag_up_2' ]
-      }
-
-      await updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, attributes)
-
-      await wait(5000)
-    })
-
-    it('Should have the video 3 updated on each server', async function () {
-      this.timeout(10000)
-
-      for (const server of servers) {
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-        const videoUpdated = videos.find(video => video.name === 'my super video updated')
-
-        expect(!!videoUpdated).to.be.true
-        expect(videoUpdated.category).to.equal(10)
-        expect(videoUpdated.categoryLabel).to.equal('Entertainment')
-        expect(videoUpdated.licence).to.equal(7)
-        expect(videoUpdated.licenceLabel).to.equal('Public Domain Dedication')
-        expect(videoUpdated.language).to.equal(13)
-        expect(videoUpdated.languageLabel).to.equal('French')
-        expect(videoUpdated.nsfw).to.be.ok
-        expect(videoUpdated.description).to.equal('my super description updated')
-        expect(dateIsValid(videoUpdated.updatedAt, 20000)).to.be.true
-
-        const res2 = await getVideo(server.url, videoUpdated.uuid)
-        const videoUpdatedDetails = res2.body
-        expect(videoUpdatedDetails.tags).to.deep.equal([ 'tag_up_1', 'tag_up_2' ])
-
-        const file = videoUpdatedDetails.files[0]
-        expect(file.magnetUri).to.have.lengthOf.above(2)
-        expect(file.resolution).to.equal(720)
-        expect(file.resolutionLabel).to.equal('720p')
-        expect(file.size).to.equal(292677)
-
-        const test = await testVideoImage(server.url, 'video_short3.webm', videoUpdated.thumbnailPath)
-        expect(test).to.equal(true)
-
-        // Avoid "duplicate torrent" errors
-        const refreshWebTorrent = true
-        const torrent = await webtorrentAdd(videoUpdatedDetails .files[0].magnetUri, refreshWebTorrent)
-        expect(torrent.files).to.be.an('array')
-        expect(torrent.files.length).to.equal(1)
-        expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-      }
-    })
-
-    it('Should remove the videos 3 and 3-2 by asking server 3', async function () {
-      this.timeout(10000)
-
-      await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id)
-      await removeVideo(servers[2].url, servers[2].accessToken, toRemove[1].id)
-
-      await wait(5000)
-    })
-
-    it('Should have videos 1 and 3 on each server', async function () {
-      for (const server of servers) {
-        const res = await getVideosList(server.url)
-
-        const videos = res.body.data
-        expect(videos).to.be.an('array')
-        expect(videos.length).to.equal(2)
-        expect(videos[0].name).not.to.equal(videos[1].name)
-        expect(videos[0].name).not.to.equal(toRemove[0].name)
-        expect(videos[1].name).not.to.equal(toRemove[0].name)
-        expect(videos[0].name).not.to.equal(toRemove[1].name)
-        expect(videos[1].name).not.to.equal(toRemove[1].name)
-
-        videoUUID = videos.find(video => video.name === 'my super name for server 1').uuid
-      }
-    })
-
-    it('Should get the same video by UUID on each server', async function () {
-      let baseVideo = null
-      for (const server of servers) {
-        const res = await getVideo(server.url, videoUUID)
-
-        const video = res.body
-
-        if (baseVideo === null) {
-          baseVideo = video
-          continue
-        }
-
-        expect(baseVideo.name).to.equal(video.name)
-        expect(baseVideo.uuid).to.equal(video.uuid)
-        expect(baseVideo.category).to.equal(video.category)
-        expect(baseVideo.language).to.equal(video.language)
-        expect(baseVideo.licence).to.equal(video.licence)
-        expect(baseVideo.category).to.equal(video.category)
-        expect(baseVideo.nsfw).to.equal(video.nsfw)
-        expect(baseVideo.accountName).to.equal(video.accountName)
-        expect(baseVideo.tags).to.deep.equal(video.tags)
-      }
-    })
-
-    it('Should get the preview from each server', async function () {
-      for (const server of servers) {
-        const res = await getVideo(server.url, videoUUID)
-        const video = res.body
-
-        const test = await testVideoImage(server.url, 'video_short1-preview.webm', video.previewPath)
-        expect(test).to.equal(true)
-      }
-    })
-  })
-
-  describe('Should comment these videos', function () {
-    it('Should add comment (threads and replies)', async function () {
-      this.timeout(25000)
-
-      {
-        const text = 'my super first comment'
-        await addVideoCommentThread(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, text)
-      }
-
-      {
-        const text = 'my super second comment'
-        await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, text)
-      }
-
-      await wait(5000)
-
-      {
-        const res = await getVideoCommentThreads(servers[1].url, videoUUID, 0, 5)
-        const threadId = res.body.data.find(c => c.text === 'my super first comment').id
-
-        const text = 'my super answer to thread 1'
-        await addVideoCommentReply(servers[ 1 ].url, servers[ 1 ].accessToken, videoUUID, threadId, text)
-      }
-
-      await wait(5000)
-
-      {
-        const res1 = await getVideoCommentThreads(servers[2].url, videoUUID, 0, 5)
-        const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
-
-        const res2 = await getVideoThreadComments(servers[2].url, videoUUID, threadId)
-        const childCommentId = res2.body.children[0].comment.id
-
-        const text3 = 'my second answer to thread 1'
-        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, threadId, text3)
-
-        const text2 = 'my super answer to answer of thread 1'
-        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, childCommentId, text2)
-      }
-
-      await wait(5000)
-    })
-
-    it('Should have these threads', async function () {
-      for (const server of servers) {
-        const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
-
-        expect(res.body.total).to.equal(2)
-        expect(res.body.data).to.be.an('array')
-        expect(res.body.data).to.have.lengthOf(2)
-
-        {
-          const comment: VideoComment = res.body.data.find(c => c.text === 'my super first comment')
-          expect(comment).to.not.be.undefined
-          expect(comment.inReplyToCommentId).to.be.null
-          expect(comment.account.name).to.equal('root')
-          expect(comment.account.host).to.equal('localhost:9001')
-          expect(comment.totalReplies).to.equal(3)
-          expect(dateIsValid(comment.createdAt as string)).to.be.true
-          expect(dateIsValid(comment.updatedAt as string)).to.be.true
-        }
-
-        {
-          const comment: VideoComment = res.body.data.find(c => c.text === 'my super second comment')
-          expect(comment).to.not.be.undefined
-          expect(comment.inReplyToCommentId).to.be.null
-          expect(comment.account.name).to.equal('root')
-          expect(comment.account.host).to.equal('localhost:9003')
-          expect(comment.totalReplies).to.equal(0)
-          expect(dateIsValid(comment.createdAt as string)).to.be.true
-          expect(dateIsValid(comment.updatedAt as string)).to.be.true
-        }
-      }
-    })
-
-    it('Should have these comments', async function () {
-      for (const server of servers) {
-        const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
-        const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
-
-        const res2 = await getVideoThreadComments(server.url, videoUUID, threadId)
-
-        const tree: VideoCommentThreadTree = res2.body
-        expect(tree.comment.text).equal('my super first comment')
-        expect(tree.comment.account.name).equal('root')
-        expect(tree.comment.account.host).equal('localhost:9001')
-        expect(tree.children).to.have.lengthOf(2)
-
-        const firstChild = tree.children[0]
-        expect(firstChild.comment.text).to.equal('my super answer to thread 1')
-        expect(firstChild.comment.account.name).equal('root')
-        expect(firstChild.comment.account.host).equal('localhost:9002')
-        expect(firstChild.children).to.have.lengthOf(1)
-
-        const childOfFirstChild = firstChild.children[0]
-        expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
-        expect(childOfFirstChild.comment.account.name).equal('root')
-        expect(childOfFirstChild.comment.account.host).equal('localhost:9003')
-        expect(childOfFirstChild.children).to.have.lengthOf(0)
-
-        const secondChild = tree.children[1]
-        expect(secondChild.comment.text).to.equal('my second answer to thread 1')
-        expect(secondChild.comment.account.name).equal('root')
-        expect(secondChild.comment.account.host).equal('localhost:9003')
-        expect(secondChild.children).to.have.lengthOf(0)
-      }
-    })
-  })
-
-  describe('With minimum parameters', function () {
-    it('Should upload and propagate the video', async function () {
-      this.timeout(50000)
-
-      const path = '/api/v1/videos/upload'
-
-      const req = request(servers[1].url)
-        .post(path)
-        .set('Accept', 'application/json')
-        .set('Authorization', 'Bearer ' + servers[1].accessToken)
-        .field('name', 'minimum parameters')
-        .field('privacy', '1')
-        .field('nsfw', 'false')
-        .field('channelId', '1')
-
-      const filePath = join(__dirname, '..', 'api', 'fixtures', 'video_short.webm')
-
-      await req.attach('videofile', filePath)
-        .expect(200)
-
-      await wait(25000)
-
-      for (const server of servers) {
-        const res = await getVideosList(server.url)
-        const video = res.body.data.find(v => v.name === 'minimum parameters')
-
-        expect(video.name).to.equal('minimum parameters')
-        expect(video.category).to.equal(null)
-        expect(video.categoryLabel).to.equal('Misc')
-        expect(video.licence).to.equal(null)
-        expect(video.licenceLabel).to.equal('Unknown')
-        expect(video.language).to.equal(null)
-        expect(video.languageLabel).to.equal('Unknown')
-        expect(video.nsfw).to.not.be.ok
-        expect(video.description).to.equal(null)
-        expect(video.serverHost).to.equal('localhost:9002')
-        expect(video.accountName).to.equal('root')
-        expect(dateIsValid(video.createdAt)).to.be.true
-        expect(dateIsValid(video.updatedAt)).to.be.true
-      }
-    })
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
new file mode 100644 (file)
index 0000000..e8846c8
--- /dev/null
@@ -0,0 +1,54 @@
+/* tslint:disable:no-unused-expression */
+
+import 'mocha'
+import * as chai from 'chai'
+const expect = chai.expect
+
+import {
+  getConfig,
+  flushTests,
+  runServer,
+  registerUser
+} from '../../utils/index'
+
+describe('Test config', function () {
+  let server = null
+
+  before(async function () {
+    this.timeout(10000)
+
+    await flushTests()
+    server = await runServer(1)
+  })
+
+  it('Should have a correct config on a server with registration enabled', async function () {
+    const res = await getConfig(server.url)
+    const data = res.body
+
+    expect(data.signup.allowed).to.be.true
+  })
+
+  it('Should have a correct config on a server with registration enabled and a users limit', async function () {
+    this.timeout(5000)
+
+    await Promise.all([
+      registerUser(server.url, 'user1', 'super password'),
+      registerUser(server.url, 'user2', 'super password'),
+      registerUser(server.url, 'user3', 'super password')
+    ])
+
+    const res = await getConfig(server.url)
+    const data = res.body
+
+    expect(data.signup.allowed).to.be.false
+  })
+
+  after(async function () {
+    process.kill(-server.app.pid)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
new file mode 100644 (file)
index 0000000..f77c0c6
--- /dev/null
@@ -0,0 +1,318 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
+
+import {
+  flushAndRunMultipleServers, flushTests, getVideosList, killallServers, ServerInfo, setAccessTokensToServers, uploadVideo,
+  wait
+} from '../../utils/index'
+import { dateIsValid, webtorrentAdd } from '../../utils/miscs/miscs'
+import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows'
+import { getUserAccessToken } from '../../utils/users/login'
+import { createUser } from '../../utils/users/users'
+import {
+  addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads,
+  getVideoThreadComments
+} from '../../utils/videos/video-comments'
+import { getVideo, rateVideo, testVideoImage } from '../../utils/videos/videos'
+
+const expect = chai.expect
+
+describe('Test follows', function () {
+  let servers: ServerInfo[] = []
+
+  before(async function () {
+    this.timeout(20000)
+
+    servers = await flushAndRunMultipleServers(3)
+
+    // Get the access tokens
+    await setAccessTokensToServers(servers)
+  })
+
+  it('Should not have followers', async function () {
+    for (const server of servers) {
+      const res = await getFollowersListPaginationAndSort(server.url, 0, 5, 'createdAt')
+      const follows = res.body.data
+
+      expect(res.body.total).to.equal(0)
+      expect(follows).to.be.an('array')
+      expect(follows.length).to.equal(0)
+    }
+  })
+
+  it('Should not have following', async function () {
+    for (const server of servers) {
+      const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
+      const follows = res.body.data
+
+      expect(res.body.total).to.equal(0)
+      expect(follows).to.be.an('array')
+      expect(follows.length).to.equal(0)
+    }
+  })
+
+  it('Should have server 1 following server 2 and 3', async function () {
+    this.timeout(10000)
+
+    await follow(servers[0].url, [ servers[1].url, servers[2].url ], servers[0].accessToken)
+
+    await wait(7000)
+  })
+
+  it('Should have 2 followings on server 1', async function () {
+    let res = await getFollowingListPaginationAndSort(servers[0].url, 0, 1, 'createdAt')
+    let follows = res.body.data
+
+    expect(res.body.total).to.equal(2)
+    expect(follows).to.be.an('array')
+    expect(follows.length).to.equal(1)
+
+    res = await getFollowingListPaginationAndSort(servers[0].url, 1, 1, 'createdAt')
+    follows = follows.concat(res.body.data)
+
+    const server2Follow = follows.find(f => f.following.host === 'localhost:9002')
+    const server3Follow = follows.find(f => f.following.host === 'localhost:9003')
+
+    expect(server2Follow).to.not.be.undefined
+    expect(server3Follow).to.not.be.undefined
+    expect(server2Follow.state).to.equal('accepted')
+    expect(server3Follow.state).to.equal('accepted')
+  })
+
+  it('Should have 0 followings on server 1 and 2', async function () {
+    for (const server of [ servers[1], servers[2] ]) {
+      const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
+      const follows = res.body.data
+
+      expect(res.body.total).to.equal(0)
+      expect(follows).to.be.an('array')
+      expect(follows.length).to.equal(0)
+    }
+  })
+
+  it('Should have 1 followers on server 2 and 3', async function () {
+    for (const server of [ servers[1], servers[2] ]) {
+      let res = await getFollowersListPaginationAndSort(server.url, 0, 1, 'createdAt')
+
+      let follows = res.body.data
+      expect(res.body.total).to.equal(1)
+      expect(follows).to.be.an('array')
+      expect(follows.length).to.equal(1)
+      expect(follows[0].follower.host).to.equal('localhost:9001')
+    }
+  })
+
+  it('Should have 0 followers on server 1', async function () {
+    const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
+    const follows = res.body.data
+
+    expect(res.body.total).to.equal(0)
+    expect(follows).to.be.an('array')
+    expect(follows.length).to.equal(0)
+  })
+
+  it('Should unfollow server 3 on server 1', async function () {
+    this.timeout(5000)
+
+    await unfollow(servers[0].url, servers[0].accessToken, servers[2])
+
+    await wait(3000)
+  })
+
+  it('Should not follow server 3 on server 1 anymore', async function () {
+    const res = await getFollowingListPaginationAndSort(servers[0].url, 0, 2, 'createdAt')
+    let follows = res.body.data
+
+    expect(res.body.total).to.equal(1)
+    expect(follows).to.be.an('array')
+    expect(follows.length).to.equal(1)
+
+    expect(follows[0].following.host).to.equal('localhost:9002')
+  })
+
+  it('Should not have server 1 as follower on server 3 anymore', async function () {
+    const res = await getFollowersListPaginationAndSort(servers[2].url, 0, 1, 'createdAt')
+
+    let follows = res.body.data
+    expect(res.body.total).to.equal(0)
+    expect(follows).to.be.an('array')
+    expect(follows.length).to.equal(0)
+  })
+
+  it('Should upload a video on server 2 ans 3 and propagate only the video of server 2', async function () {
+    this.timeout(10000)
+
+    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'server2' })
+    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3' })
+
+    await wait(5000)
+
+    let res = await getVideosList(servers[0].url)
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data[0].name).to.equal('server2')
+
+    res = await getVideosList(servers[1].url)
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data[0].name).to.equal('server2')
+
+    res = await getVideosList(servers[2].url)
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data[0].name).to.equal('server3')
+  })
+
+  it('Should propagate previous uploaded videos on a new following', async function () {
+    this.timeout(20000)
+
+    const video4Attributes = {
+      name: 'server3-4',
+      category: 2,
+      nsfw: true,
+      licence: 6,
+      tags: [ 'tag1', 'tag2', 'tag3' ]
+    }
+
+    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-2' })
+    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-3' })
+    await uploadVideo(servers[2].url, servers[2].accessToken, video4Attributes)
+    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-5' })
+    await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3-6' })
+
+    {
+      const user = { username: 'captain', password: 'password' }
+      await createUser(servers[2].url, servers[2].accessToken, user.username, user.password)
+      const userAccessToken = await getUserAccessToken(servers[2], user)
+
+      const resVideos = await getVideosList(servers[ 2 ].url)
+      const video4 = resVideos.body.data.find(v => v.name === 'server3-4')
+
+      {
+        await rateVideo(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, 'like')
+        await rateVideo(servers[ 2 ].url, userAccessToken, video4.id, 'dislike')
+      }
+
+      {
+        const text = 'my super first comment'
+        const res = await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, text)
+        const threadId = res.body.comment.id
+
+        const text1 = 'my super answer to thread 1'
+        const childCommentRes = await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text1)
+        const childCommentId = childCommentRes.body.comment.id
+
+        const text2 = 'my super answer to answer of thread 1'
+        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, childCommentId, text2)
+
+        const text3 = 'my second answer to thread 1'
+        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text3)
+      }
+    }
+
+    await wait(5000)
+
+    // Server 1 follows server 3
+    await follow(servers[0].url, [ servers[2].url ], servers[0].accessToken)
+
+    await wait(7000)
+
+    let res = await getVideosList(servers[0].url)
+    expect(res.body.total).to.equal(7)
+
+    const video2 = res.body.data.find(v => v.name === 'server3-2')
+    const video4 = res.body.data.find(v => v.name === 'server3-4')
+    const video6 = res.body.data.find(v => v.name === 'server3-6')
+
+    expect(video2).to.not.be.undefined
+    expect(video4).to.not.be.undefined
+    expect(video6).to.not.be.undefined
+
+    const res2 = await getVideo(servers[0].url, video4.id)
+    const videoDetails = res2.body
+
+    expect(videoDetails.name).to.equal('server3-4')
+    expect(videoDetails.category).to.equal(2)
+    expect(videoDetails.categoryLabel).to.equal('Films')
+    expect(videoDetails.licence).to.equal(6)
+    expect(videoDetails.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
+    expect(videoDetails.language).to.equal(3)
+    expect(videoDetails.languageLabel).to.equal('Mandarin')
+    expect(videoDetails.nsfw).to.be.ok
+    expect(videoDetails.description).to.equal('my super description')
+    expect(videoDetails.serverHost).to.equal('localhost:9003')
+    expect(videoDetails.accountName).to.equal('root')
+    expect(videoDetails.likes).to.equal(1)
+    expect(videoDetails.dislikes).to.equal(1)
+    expect(videoDetails.isLocal).to.be.false
+    expect(videoDetails.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
+    expect(dateIsValid(videoDetails.createdAt)).to.be.true
+    expect(dateIsValid(videoDetails.updatedAt)).to.be.true
+    expect(videoDetails.files).to.have.lengthOf(1)
+
+    const file = videoDetails.files[0]
+    const magnetUri = file.magnetUri
+    expect(file.magnetUri).to.have.lengthOf.above(2)
+    expect(file.torrentUrl).to.equal(`${servers[2].url}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
+    expect(file.fileUrl).to.equal(`${servers[2].url}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`)
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
+    expect(file.size).to.equal(218910)
+
+    const test = await testVideoImage(servers[2].url, 'video_short.webm', videoDetails.thumbnailPath)
+    expect(test).to.equal(true)
+
+    const torrent = await webtorrentAdd(magnetUri)
+    expect(torrent.files).to.be.an('array')
+    expect(torrent.files.length).to.equal(1)
+    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+
+    {
+      const res1 = await getVideoCommentThreads(servers[0].url, video4.id, 0, 5)
+
+      expect(res1.body.total).to.equal(1)
+      expect(res1.body.data).to.be.an('array')
+      expect(res1.body.data).to.have.lengthOf(1)
+
+      const comment: VideoComment = res1.body.data[0]
+      expect(comment.inReplyToCommentId).to.be.null
+      expect(comment.text).equal('my super first comment')
+      expect(comment.videoId).to.equal(video4.id)
+      expect(comment.id).to.equal(comment.threadId)
+      expect(comment.account.name).to.equal('root')
+      expect(comment.account.host).to.equal('localhost:9003')
+      expect(comment.totalReplies).to.equal(3)
+      expect(dateIsValid(comment.createdAt as string)).to.be.true
+      expect(dateIsValid(comment.updatedAt as string)).to.be.true
+
+      const threadId = comment.threadId
+
+      const res2 = await getVideoThreadComments(servers[0].url, video4.id, threadId)
+
+      const tree: VideoCommentThreadTree = res2.body
+      expect(tree.comment.text).equal('my super first comment')
+      expect(tree.children).to.have.lengthOf(2)
+
+      const firstChild = tree.children[0]
+      expect(firstChild.comment.text).to.equal('my super answer to thread 1')
+      expect(firstChild.children).to.have.lengthOf(1)
+
+      const childOfFirstChild = firstChild.children[0]
+      expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
+      expect(childOfFirstChild.children).to.have.lengthOf(0)
+
+      const secondChild = tree.children[1]
+      expect(secondChild.comment.text).to.equal('my second answer to thread 1')
+      expect(secondChild.children).to.have.lengthOf(0)
+    }
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/server/jobs.ts b/server/tests/api/server/jobs.ts
new file mode 100644 (file)
index 0000000..2e17e71
--- /dev/null
@@ -0,0 +1,64 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import { flushTests, killallServers, ServerInfo, setAccessTokensToServers, wait } from '../../utils/index'
+import { doubleFollow } from '../../utils/server/follows'
+import { getJobsList, getJobsListPaginationAndSort } from '../../utils/server/jobs'
+import { flushAndRunMultipleServers } from '../../utils/server/servers'
+import { uploadVideo } from '../../utils/videos/videos'
+import { dateIsValid } from '../../utils/miscs/miscs'
+
+const expect = chai.expect
+
+describe('Test jobs', function () {
+  let servers: ServerInfo[]
+
+  before(async function () {
+    this.timeout(30000)
+
+    servers = await flushAndRunMultipleServers(2)
+
+    await setAccessTokensToServers(servers)
+
+    // Server 1 and server 2 follow each other
+    await doubleFollow(servers[0], servers[1])
+  })
+
+  it('Should create some jobs', async function () {
+    this.timeout(30000)
+
+    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' })
+    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' })
+
+    await wait(15000)
+  })
+
+  it('Should list jobs', async function () {
+    const res = await getJobsList(servers[1].url, servers[1].accessToken)
+    expect(res.body.total).to.be.above(2)
+    expect(res.body.data).to.have.length.above(2)
+  })
+
+  it('Should list jobs with sort and pagination', async function () {
+    const res = await getJobsListPaginationAndSort(servers[1].url, servers[1].accessToken, 4, 1, 'createdAt')
+    expect(res.body.total).to.be.above(2)
+    expect(res.body.data).to.have.lengthOf(1)
+
+    const job = res.body.data[0]
+    expect(job.state).to.equal('success')
+    expect(job.category).to.equal('transcoding')
+    expect(job.handlerName).to.have.length.above(3)
+    expect(dateIsValid(job.createdAt)).to.be.true
+    expect(dateIsValid(job.updatedAt)).to.be.true
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/services.ts b/server/tests/api/services.ts
deleted file mode 100644 (file)
index 4d480c3..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import 'mocha'
-import * as chai from 'chai'
-const expect = chai.expect
-
-import {
-  ServerInfo,
-  flushTests,
-  uploadVideo,
-  getVideosList,
-  setAccessTokensToServers,
-  killallServers,
-  getOEmbed
-} from '../utils'
-import { runServer } from '../utils/servers'
-
-describe('Test services', function () {
-  let server: ServerInfo = null
-
-  before(async function () {
-    this.timeout(10000)
-
-    await flushTests()
-
-    server = await runServer(1)
-
-    await setAccessTokensToServers([ server ])
-
-    const videoAttributes = {
-      name: 'my super name'
-    }
-    await uploadVideo(server.url, server.accessToken, videoAttributes)
-
-    const res = await getVideosList(server.url)
-    server.video = res.body.data[0]
-  })
-
-  it('Should have a valid oEmbed response', async function () {
-    const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid
-
-    const res = await getOEmbed(server.url, oembedUrl)
-    const expectedHtml = `<iframe width="560" height="315" src="http://localhost:9001/videos/embed/${server.video.uuid}" ` +
-                         'frameborder="0" allowfullscreen></iframe>'
-    const expectedThumbnailUrl = 'http://localhost:9001/static/previews/' + server.video.uuid + '.jpg'
-
-    expect(res.body.html).to.equal(expectedHtml)
-    expect(res.body.title).to.equal(server.video.name)
-    expect(res.body.author_name).to.equal(server.video.accountName)
-    expect(res.body.width).to.equal(560)
-    expect(res.body.height).to.equal(315)
-    expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl)
-    expect(res.body.thumbnail_width).to.equal(560)
-    expect(res.body.thumbnail_height).to.equal(315)
-  })
-
-  it('Should have a valid oEmbed response with small max height query', async function () {
-    const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid
-    const format = 'json'
-    const maxHeight = 50
-    const maxWidth = 50
-
-    const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth)
-    const expectedHtml = `<iframe width="50" height="50" src="http://localhost:9001/videos/embed/${server.video.uuid}" ` +
-                         'frameborder="0" allowfullscreen></iframe>'
-
-    expect(res.body.html).to.equal(expectedHtml)
-    expect(res.body.title).to.equal(server.video.name)
-    expect(res.body.author_name).to.equal(server.video.accountName)
-    expect(res.body.height).to.equal(50)
-    expect(res.body.width).to.equal(50)
-    expect(res.body).to.not.have.property('thumbnail_url')
-    expect(res.body).to.not.have.property('thumbnail_width')
-    expect(res.body).to.not.have.property('thumbnail_height')
-  })
-
-  after(async function () {
-    killallServers([ server ])
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/single-server.ts b/server/tests/api/single-server.ts
deleted file mode 100644 (file)
index 7f4351f..0000000
+++ /dev/null
@@ -1,701 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import { keyBy } from 'lodash'
-import 'mocha'
-import { join } from 'path'
-import {
-  dateIsValid,
-  flushTests,
-  getVideo,
-  getVideoCategories,
-  getVideoLanguages,
-  getVideoLicences,
-  getVideoPrivacies,
-  getVideosList,
-  getVideosListPagination,
-  getVideosListSort,
-  killallServers,
-  rateVideo,
-  readdirPromise,
-  removeVideo,
-  runServer,
-  searchVideo,
-  searchVideoWithPagination,
-  searchVideoWithSort,
-  ServerInfo,
-  setAccessTokensToServers,
-  testVideoImage,
-  updateVideo,
-  uploadVideo,
-  wait,
-  webtorrentAdd
-} from '../utils'
-import { viewVideo } from '../utils/videos'
-
-const expect = chai.expect
-
-describe('Test a single server', function () {
-  let server: ServerInfo = null
-  let videoId = -1
-  let videoUUID = ''
-  let videosListBase: any[] = null
-
-  before(async function () {
-    this.timeout(10000)
-
-    await flushTests()
-
-    server = await runServer(1)
-
-    await setAccessTokensToServers([ server ])
-  })
-
-  it('Should list video categories', async function () {
-    const res = await getVideoCategories(server.url)
-
-    const categories = res.body
-    expect(Object.keys(categories)).to.have.length.above(10)
-
-    expect(categories[11]).to.equal('News')
-  })
-
-  it('Should list video licences', async function () {
-    const res = await getVideoLicences(server.url)
-
-    const licences = res.body
-    expect(Object.keys(licences)).to.have.length.above(5)
-
-    expect(licences[3]).to.equal('Attribution - No Derivatives')
-  })
-
-  it('Should list video languages', async function () {
-    const res = await getVideoLanguages(server.url)
-
-    const languages = res.body
-    expect(Object.keys(languages)).to.have.length.above(5)
-
-    expect(languages[3]).to.equal('Mandarin')
-  })
-
-  it('Should list video privacies', async function () {
-    const res = await getVideoPrivacies(server.url)
-
-    const privacies = res.body
-    expect(Object.keys(privacies)).to.have.length.at.least(3)
-
-    expect(privacies[3]).to.equal('Private')
-  })
-
-  it('Should not have videos', async function () {
-    const res = await getVideosList(server.url)
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(0)
-  })
-
-  it('Should upload the video', async function () {
-    const videoAttributes = {
-      name: 'my super name',
-      category: 2,
-      nsfw: true,
-      licence: 6,
-      tags: [ 'tag1', 'tag2', 'tag3' ]
-    }
-    const res = await uploadVideo(server.url, server.accessToken, videoAttributes)
-    expect(res.body.video).to.not.be.undefined
-    expect(res.body.video.id).to.equal(1)
-    expect(res.body.video.uuid).to.have.length.above(5)
-  })
-
-  it('Should seed the uploaded video', async function () {
-    // Yes, this could be long
-    this.timeout(60000)
-
-    const res = await getVideosList(server.url)
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(1)
-
-    const video = res.body.data[0]
-    expect(video.name).to.equal('my super name')
-    expect(video.category).to.equal(2)
-    expect(video.categoryLabel).to.equal('Films')
-    expect(video.licence).to.equal(6)
-    expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
-    expect(video.language).to.equal(3)
-    expect(video.languageLabel).to.equal('Mandarin')
-    expect(video.nsfw).to.be.ok
-    expect(video.description).to.equal('my super description')
-    expect(video.serverHost).to.equal('localhost:9001')
-    expect(video.accountName).to.equal('root')
-    expect(video.isLocal).to.be.true
-    expect(dateIsValid(video.createdAt)).to.be.true
-    expect(dateIsValid(video.updatedAt)).to.be.true
-
-    const res2 = await getVideo(server.url, res.body.data[0].id)
-    const videoDetails = res2.body
-
-    expect(videoDetails.files).to.have.lengthOf(1)
-
-    const file = videoDetails.files[0]
-    const magnetUri = file.magnetUri
-    expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.torrentUrl).to.equal(`${server.url}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
-    expect(file.fileUrl).to.equal(`${server.url}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`)
-    expect(file.resolution).to.equal(720)
-    expect(file.resolutionLabel).to.equal('720p')
-    expect(file.size).to.equal(218910)
-
-    const test = await testVideoImage(server.url, 'video_short.webm', videoDetails.thumbnailPath)
-    expect(test).to.equal(true)
-
-    videoId = videoDetails.id
-    videoUUID = videoDetails.uuid
-
-    const torrent = await webtorrentAdd(magnetUri)
-    expect(torrent.files).to.be.an('array')
-    expect(torrent.files.length).to.equal(1)
-    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-  })
-
-  it('Should get the video', async function () {
-    // Yes, this could be long
-    this.timeout(60000)
-
-    const res = await getVideo(server.url, videoId)
-
-    const video = res.body
-    expect(video.name).to.equal('my super name')
-    expect(video.category).to.equal(2)
-    expect(video.categoryLabel).to.equal('Films')
-    expect(video.licence).to.equal(6)
-    expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
-    expect(video.language).to.equal(3)
-    expect(video.languageLabel).to.equal('Mandarin')
-    expect(video.nsfw).to.be.ok
-    expect(video.description).to.equal('my super description')
-    expect(video.serverHost).to.equal('localhost:9001')
-    expect(video.accountName).to.equal('root')
-    expect(video.isLocal).to.be.true
-    expect(dateIsValid(video.createdAt)).to.be.true
-    expect(dateIsValid(video.updatedAt)).to.be.true
-    expect(video.channel.name).to.equal('Default root channel')
-    expect(video.channel.isLocal).to.be.true
-    expect(dateIsValid(video.channel.createdAt)).to.be.true
-    expect(dateIsValid(video.channel.updatedAt)).to.be.true
-
-    expect(video.files).to.have.lengthOf(1)
-
-    const file = video.files[0]
-    expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(720)
-    expect(file.resolutionLabel).to.equal('720p')
-    expect(file.size).to.equal(218910)
-
-    const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
-    expect(test).to.equal(true)
-
-    // Wait the async views increment
-    await wait(500)
-  })
-
-  it('Should get the video by UUID', async function () {
-    // Yes, this could be long
-    this.timeout(60000)
-
-    const res = await getVideo(server.url, videoUUID)
-
-    const video = res.body
-    expect(video.name).to.equal('my super name')
-
-    // Wait the async views increment
-    await wait(500)
-  })
-
-  it('Should have the views updated', async function () {
-    await viewVideo(server.url, videoId)
-    await viewVideo(server.url, videoId)
-    await viewVideo(server.url, videoId)
-
-    const res = await getVideo(server.url, videoId)
-
-    const video = res.body
-    expect(video.views).to.equal(3)
-  })
-
-  it('Should search the video by name', async function () {
-    const res = await searchVideo(server.url, 'my')
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(1)
-
-    const video = res.body.data[0]
-    expect(video.name).to.equal('my super name')
-    expect(video.category).to.equal(2)
-    expect(video.categoryLabel).to.equal('Films')
-    expect(video.licence).to.equal(6)
-    expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
-    expect(video.language).to.equal(3)
-    expect(video.languageLabel).to.equal('Mandarin')
-    expect(video.nsfw).to.be.ok
-    expect(video.description).to.equal('my super description')
-    expect(video.serverHost).to.equal('localhost:9001')
-    expect(video.accountName).to.equal('root')
-    expect(video.isLocal).to.be.true
-    expect(dateIsValid(video.createdAt)).to.be.true
-    expect(dateIsValid(video.updatedAt)).to.be.true
-
-    const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
-    expect(test).to.equal(true)
-  })
-
-  // Not implemented yet
-  // it('Should search the video by serverHost', async function () {
-  //     const res = await   videosUtils.searchVideo(server.url, '9001', 'host')
-
-  //     expect(res.body.total).to.equal(1)
-  //     expect(res.body.data).to.be.an('array')
-  //     expect(res.body.data.length).to.equal(1)
-
-  //     const video = res.body.data[0]
-  //     expect(video.name).to.equal('my super name')
-  //     expect(video.description).to.equal('my super description')
-  //     expect(video.serverHost).to.equal('localhost:9001')
-  //     expect(video.author).to.equal('root')
-  //     expect(video.isLocal).to.be.true
-  //     expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
-  //     expect(dateIsValid(video.createdAt)).to.be.true
-  //     expect(dateIsValid(video.updatedAt)).to.be.true
-
-  //     const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
-  //       expect(test).to.equal(true)
-
-  //       done()
-  //     })
-  //   })
-  // })
-
-  // Not implemented yet
-  // it('Should search the video by tag', async function () {
-  //   const res = await searchVideo(server.url, 'tag1')
-  //
-  //   expect(res.body.total).to.equal(1)
-  //   expect(res.body.data).to.be.an('array')
-  //   expect(res.body.data.length).to.equal(1)
-  //
-  //   const video = res.body.data[0]
-  //   expect(video.name).to.equal('my super name')
-  //   expect(video.category).to.equal(2)
-  //   expect(video.categoryLabel).to.equal('Films')
-  //   expect(video.licence).to.equal(6)
-  //   expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
-  //   expect(video.language).to.equal(3)
-  //   expect(video.languageLabel).to.equal('Mandarin')
-  //   expect(video.nsfw).to.be.ok
-  //   expect(video.description).to.equal('my super description')
-  //   expect(video.serverHost).to.equal('localhost:9001')
-  //   expect(video.accountName).to.equal('root')
-  //   expect(video.isLocal).to.be.true
-  //   expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
-  //   expect(dateIsValid(video.createdAt)).to.be.true
-  //   expect(dateIsValid(video.updatedAt)).to.be.true
-  //
-  //   const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
-  //   expect(test).to.equal(true)
-  // })
-
-  it('Should not find a search by name', async function () {
-    const res = await searchVideo(server.url, 'hello')
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(0)
-  })
-
-  // Not implemented yet
-  // it('Should not find a search by author', async function () {
-  //   const res = await searchVideo(server.url, 'hello')
-  //
-  //   expect(res.body.total).to.equal(0)
-  //   expect(res.body.data).to.be.an('array')
-  //   expect(res.body.data.length).to.equal(0)
-  // })
-  //
-  // Not implemented yet
-  // it('Should not find a search by tag', async function () {
-  //   const res = await searchVideo(server.url, 'hello')
-  //
-  //   expect(res.body.total).to.equal(0)
-  //   expect(res.body.data).to.be.an('array')
-  //   expect(res.body.data.length).to.equal(0)
-  // })
-
-  it('Should remove the video', async function () {
-    await removeVideo(server.url, server.accessToken, videoId)
-
-    const files1 = await readdirPromise(join(__dirname, '..', '..', '..', 'test1/videos/'))
-    expect(files1).to.have.lengthOf(0)
-
-    const files2 = await readdirPromise(join(__dirname, '..', '..', '..', 'test1/thumbnails/'))
-    expect(files2).to.have.lengthOf(0)
-  })
-
-  it('Should not have videos', async function () {
-    const res = await getVideosList(server.url)
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(0)
-  })
-
-  it('Should upload 6 videos', async function () {
-    this.timeout(25000)
-
-    const videos = [
-      'video_short.mp4', 'video_short.ogv', 'video_short.webm',
-      'video_short1.webm', 'video_short2.webm', 'video_short3.webm'
-    ]
-
-    const tasks: Promise<any>[] = []
-    for (const video of videos) {
-      const videoAttributes = {
-        name: video + ' name',
-        description: video + ' description',
-        category: 2,
-        licence: 1,
-        language: 1,
-        nsfw: true,
-        tags: [ 'tag1', 'tag2', 'tag3' ],
-        fixture: video
-      }
-
-      const p = uploadVideo(server.url, server.accessToken, videoAttributes)
-      tasks.push(p)
-    }
-
-    await Promise.all(tasks)
-  })
-
-  it('Should have the correct durations', async function () {
-    const res = await getVideosList(server.url)
-
-    expect(res.body.total).to.equal(6)
-    const videos = res.body.data
-    expect(videos).to.be.an('array')
-    expect(videos).to.have.lengthOf(6)
-
-    const videosByName = keyBy<{ duration: number }>(videos, 'name')
-    expect(videosByName['video_short.mp4 name'].duration).to.equal(5)
-    expect(videosByName['video_short.ogv name'].duration).to.equal(5)
-    expect(videosByName['video_short.webm name'].duration).to.equal(5)
-    expect(videosByName['video_short1.webm name'].duration).to.equal(10)
-    expect(videosByName['video_short2.webm name'].duration).to.equal(5)
-    expect(videosByName['video_short3.webm name'].duration).to.equal(5)
-  })
-
-  it('Should have the correct thumbnails', async function () {
-    const res = await getVideosList(server.url)
-
-    const videos = res.body.data
-    // For the next test
-    videosListBase = videos
-
-    for (const video of videos) {
-      const videoName = video.name.replace(' name', '')
-      const test = await testVideoImage(server.url, videoName, video.thumbnailPath)
-
-      expect(test).to.equal(true)
-    }
-  })
-
-  it('Should list only the two first videos', async function () {
-    const res = await getVideosListPagination(server.url, 0, 2, 'name')
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(6)
-    expect(videos.length).to.equal(2)
-    expect(videos[0].name).to.equal(videosListBase[0].name)
-    expect(videos[1].name).to.equal(videosListBase[1].name)
-  })
-
-  it('Should list only the next three videos', async function () {
-    const res = await getVideosListPagination(server.url, 2, 3, 'name')
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(6)
-    expect(videos.length).to.equal(3)
-    expect(videos[0].name).to.equal(videosListBase[2].name)
-    expect(videos[1].name).to.equal(videosListBase[3].name)
-    expect(videos[2].name).to.equal(videosListBase[4].name)
-  })
-
-  it('Should list the last video', async function () {
-    const res = await getVideosListPagination(server.url, 5, 6, 'name')
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(6)
-    expect(videos.length).to.equal(1)
-    expect(videos[0].name).to.equal(videosListBase[5].name)
-  })
-
-  it('Should search the first video', async function () {
-    const res = await searchVideoWithPagination(server.url, 'webm', 0, 1, 'name')
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(4)
-    expect(videos.length).to.equal(1)
-    expect(videos[0].name).to.equal('video_short1.webm name')
-  })
-
-  it('Should search the last two videos', async function () {
-    const res = await searchVideoWithPagination(server.url, 'webm', 2, 2, 'name')
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(4)
-    expect(videos.length).to.equal(2)
-    expect(videos[0].name).to.equal('video_short3.webm name')
-    expect(videos[1].name).to.equal('video_short.webm name')
-  })
-
-  it('Should search all the webm videos', async function () {
-    const res = await searchVideoWithPagination(server.url, 'webm', 0, 15)
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(4)
-    expect(videos.length).to.equal(4)
-  })
-
-  // Not implemented yet
-  // it('Should search all the root author videos', async function () {
-  //   const res = await searchVideoWithPagination(server.url, 'root', 0, 15)
-  //
-  //   const videos = res.body.data
-  //   expect(res.body.total).to.equal(6)
-  //   expect(videos.length).to.equal(6)
-  // })
-
-  // Not implemented yet
-  // it('Should search all the 9001 port videos', async function () {
-  // const res = await   videosUtils.searchVideoWithPagination(server.url, '9001', 'host', 0, 15)
-
-  //     const videos = res.body.data
-  //     expect(res.body.total).to.equal(6)
-  //     expect(videos.length).to.equal(6)
-
-  //     done()
-  //   })
-  // })
-
-  // it('Should search all the localhost videos', async function () {
-  // const res = await   videosUtils.searchVideoWithPagination(server.url, 'localhost', 'host', 0, 15)
-
-  //     const videos = res.body.data
-  //     expect(res.body.total).to.equal(6)
-  //     expect(videos.length).to.equal(6)
-
-  //     done()
-  //   })
-  // })
-
-  it('Should list and sort by name in descending order', async function () {
-    const res = await getVideosListSort(server.url, '-name')
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(6)
-    expect(videos.length).to.equal(6)
-    expect(videos[0].name).to.equal('video_short.webm name')
-    expect(videos[1].name).to.equal('video_short.ogv name')
-    expect(videos[2].name).to.equal('video_short.mp4 name')
-    expect(videos[3].name).to.equal('video_short3.webm name')
-    expect(videos[4].name).to.equal('video_short2.webm name')
-    expect(videos[5].name).to.equal('video_short1.webm name')
-  })
-
-  it('Should search and sort by name in ascending order', async function () {
-    const res = await searchVideoWithSort(server.url, 'webm', 'name')
-
-    const videos = res.body.data
-    expect(res.body.total).to.equal(4)
-    expect(videos.length).to.equal(4)
-
-    expect(videos[0].name).to.equal('video_short1.webm name')
-    expect(videos[1].name).to.equal('video_short2.webm name')
-    expect(videos[2].name).to.equal('video_short3.webm name')
-    expect(videos[3].name).to.equal('video_short.webm name')
-
-    videoId = videos[2].id
-  })
-
-  it('Should update a video', async function () {
-    const attributes = {
-      name: 'my super video updated',
-      category: 4,
-      licence: 2,
-      language: 5,
-      nsfw: false,
-      description: 'my super description updated',
-      tags: [ 'tagup1', 'tagup2' ]
-    }
-    await updateVideo(server.url, server.accessToken, videoId, attributes)
-  })
-
-  it('Should have the video updated', async function () {
-    this.timeout(60000)
-
-    const res = await getVideo(server.url, videoId)
-
-    const video = res.body
-
-    expect(video.name).to.equal('my super video updated')
-    expect(video.category).to.equal(4)
-    expect(video.categoryLabel).to.equal('Art')
-    expect(video.licence).to.equal(2)
-    expect(video.licenceLabel).to.equal('Attribution - Share Alike')
-    expect(video.language).to.equal(5)
-    expect(video.languageLabel).to.equal('Arabic')
-    expect(video.nsfw).to.be.ok
-    expect(video.description).to.equal('my super description updated')
-    expect(video.serverHost).to.equal('localhost:9001')
-    expect(video.accountName).to.equal('root')
-    expect(video.account.name).to.equal('root')
-    expect(video.isLocal).to.be.true
-    expect(video.tags).to.deep.equal([ 'tagup1', 'tagup2' ])
-    expect(dateIsValid(video.createdAt)).to.be.true
-    expect(dateIsValid(video.updatedAt)).to.be.true
-
-    expect(video.channel.name).to.equal('Default root channel')
-    expect(video.channel.isLocal).to.be.true
-    expect(dateIsValid(video.channel.createdAt)).to.be.true
-    expect(dateIsValid(video.channel.updatedAt)).to.be.true
-
-    expect(video.files).to.have.lengthOf(1)
-
-    const file = video.files[0]
-    const magnetUri = file.magnetUri
-    expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(720)
-    expect(file.resolutionLabel).to.equal('720p')
-    expect(file.size).to.equal(292677)
-
-    const test = await testVideoImage(server.url, 'video_short3.webm', video.thumbnailPath)
-    expect(test).to.equal(true)
-
-    const torrent = await webtorrentAdd(magnetUri)
-    expect(torrent.files).to.be.an('array')
-    expect(torrent.files.length).to.equal(1)
-    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
-  })
-
-  it('Should update only the tags of a video', async function () {
-    const attributes = {
-      tags: [ 'tag1', 'tag2', 'supertag' ]
-    }
-
-    await updateVideo(server.url, server.accessToken, videoId, attributes)
-
-    const res = await getVideo(server.url, videoId)
-    const video = res.body
-
-    expect(video.name).to.equal('my super video updated')
-    expect(video.category).to.equal(4)
-    expect(video.categoryLabel).to.equal('Art')
-    expect(video.licence).to.equal(2)
-    expect(video.licenceLabel).to.equal('Attribution - Share Alike')
-    expect(video.language).to.equal(5)
-    expect(video.languageLabel).to.equal('Arabic')
-    expect(video.nsfw).to.be.ok
-    expect(video.description).to.equal('my super description updated')
-    expect(video.serverHost).to.equal('localhost:9001')
-    expect(video.accountName).to.equal('root')
-    expect(video.isLocal).to.be.true
-    expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ])
-    expect(dateIsValid(video.createdAt)).to.be.true
-    expect(dateIsValid(video.updatedAt)).to.be.true
-
-    expect(video.channel.name).to.equal('Default root channel')
-    expect(video.channel.isLocal).to.be.true
-    expect(dateIsValid(video.channel.createdAt)).to.be.true
-    expect(dateIsValid(video.channel.updatedAt)).to.be.true
-
-    expect(video.files).to.have.lengthOf(1)
-
-    const file = video.files[0]
-    expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(720)
-    expect(file.resolutionLabel).to.equal('720p')
-    expect(file.size).to.equal(292677)
-  })
-
-  it('Should update only the description of a video', async function () {
-    const attributes = {
-      description: 'hello everybody'
-    }
-
-    await updateVideo(server.url, server.accessToken, videoId, attributes)
-
-    const res = await getVideo(server.url, videoId)
-    const video = res.body
-
-    expect(video.name).to.equal('my super video updated')
-    expect(video.category).to.equal(4)
-    expect(video.categoryLabel).to.equal('Art')
-    expect(video.licence).to.equal(2)
-    expect(video.licenceLabel).to.equal('Attribution - Share Alike')
-    expect(video.language).to.equal(5)
-    expect(video.languageLabel).to.equal('Arabic')
-    expect(video.nsfw).to.be.ok
-    expect(video.description).to.equal('hello everybody')
-    expect(video.serverHost).to.equal('localhost:9001')
-    expect(video.accountName).to.equal('root')
-    expect(video.isLocal).to.be.true
-    expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ])
-    expect(dateIsValid(video.createdAt)).to.be.true
-    expect(dateIsValid(video.updatedAt)).to.be.true
-
-    expect(video.channel.name).to.equal('Default root channel')
-    expect(video.channel.isLocal).to.be.true
-    expect(dateIsValid(video.channel.createdAt)).to.be.true
-    expect(dateIsValid(video.channel.updatedAt)).to.be.true
-
-    expect(video.files).to.have.lengthOf(1)
-
-    const file = video.files[0]
-    expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(720)
-    expect(file.resolutionLabel).to.equal('720p')
-    expect(file.size).to.equal(292677)
-  })
-
-  it('Should like a video', async function () {
-    await rateVideo(server.url, server.accessToken, videoId, 'like')
-
-    const res = await getVideo(server.url, videoId)
-    const video = res.body
-
-    expect(video.likes).to.equal(1)
-    expect(video.dislikes).to.equal(0)
-  })
-
-  it('Should dislike the same video', async function () {
-    await rateVideo(server.url, server.accessToken, videoId, 'dislike')
-
-    const res = await getVideo(server.url, videoId)
-    const video = res.body
-
-    expect(video.likes).to.equal(0)
-    expect(video.dislikes).to.equal(1)
-  })
-
-  after(async function () {
-    killallServers([ server ])
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/users.ts b/server/tests/api/users.ts
deleted file mode 100644 (file)
index 67e4cc8..0000000
+++ /dev/null
@@ -1,534 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import { UserRole } from '../../../shared'
-import {
-  createUser,
-  flushTests,
-  getBlacklistedVideosList,
-  getMyUserInformation,
-  getUserInformation,
-  getUsersList,
-  getUsersListPaginationAndSort,
-  getUserVideoRating,
-  getVideosList,
-  killallServers,
-  login,
-  loginAndGetAccessToken,
-  makePutBodyRequest,
-  rateVideo,
-  registerUser,
-  removeUser,
-  removeVideo,
-  runServer,
-  ServerInfo,
-  updateMyUser,
-  updateUser,
-  uploadVideo
-} from '../utils'
-import { follow } from '../utils/follows'
-import { getMyVideos } from '../utils/videos'
-import { setAccessTokensToServers } from '../utils/login'
-
-const expect = chai.expect
-
-describe('Test users', function () {
-  let server: ServerInfo
-  let accessToken: string
-  let accessTokenUser: string
-  let videoId: number
-  let userId: number
-
-  before(async function () {
-    this.timeout(10000)
-
-    await flushTests()
-    server = await runServer(1)
-
-    await setAccessTokensToServers([ server ])
-  })
-
-  it('Should create a new client')
-
-  it('Should return the first client')
-
-  it('Should remove the last client')
-
-  it('Should not login with an invalid client id', async function () {
-    const client = { id: 'client', secret: server.client.secret }
-    const res = await login(server.url, client, server.user, 400)
-
-    expect(res.body.error)
-      .to
-      .equal('invalid_client')
-  })
-
-  it('Should not login with an invalid client secret', async function () {
-    const client = { id: server.client.id, secret: 'coucou' }
-    const res = await login(server.url, client, server.user, 400)
-
-    expect(res.body.error)
-      .to
-      .equal('invalid_client')
-  })
-
-  it('Should not login with an invalid username', async function () {
-    const user = { username: 'captain crochet', password: server.user.password }
-    const res = await login(server.url, server.client, user, 400)
-
-    expect(res.body.error)
-      .to
-      .equal('invalid_grant')
-  })
-
-  it('Should not login with an invalid password', async function () {
-    const user = { username: server.user.username, password: 'mew_three' }
-    const res = await login(server.url, server.client, user, 400)
-
-    expect(res.body.error)
-      .to
-      .equal('invalid_grant')
-  })
-
-  it('Should not be able to upload a video', async function () {
-    accessToken = 'my_super_token'
-
-    const videoAttributes = {}
-    await uploadVideo(server.url, accessToken, videoAttributes, 401)
-  })
-
-  it('Should not be able to follow', async function () {
-    accessToken = 'my_super_token'
-    await follow(server.url, [ 'http://example.com' ], accessToken, 401)
-  })
-
-  it('Should not be able to unfollow')
-
-  it('Should be able to login', async function () {
-    const res = await login(server.url, server.client, server.user, 200)
-
-    accessToken = res.body.access_token
-  })
-
-  it('Should upload the video with the correct token', async function () {
-    const videoAttributes = {}
-    await uploadVideo(server.url, accessToken, videoAttributes)
-    const res = await getVideosList(server.url)
-    const video = res.body.data[ 0 ]
-
-    expect(video.accountName)
-      .to
-      .equal('root')
-    videoId = video.id
-  })
-
-  it('Should upload the video again with the correct token', async function () {
-    const videoAttributes = {}
-    await uploadVideo(server.url, accessToken, videoAttributes)
-  })
-
-  it('Should retrieve a video rating', async function () {
-    await rateVideo(server.url, accessToken, videoId, 'like')
-    const res = await getUserVideoRating(server.url, accessToken, videoId)
-    const rating = res.body
-
-    expect(rating.videoId)
-      .to
-      .equal(videoId)
-    expect(rating.rating)
-      .to
-      .equal('like')
-  })
-
-  it('Should not be able to remove the video with an incorrect token', async function () {
-    await removeVideo(server.url, 'bad_token', videoId, 401)
-  })
-
-  it('Should not be able to remove the video with the token of another account')
-
-  it('Should be able to remove the video with the correct token', async function () {
-    await removeVideo(server.url, accessToken, videoId)
-  })
-
-  it('Should logout (revoke token)')
-
-  it('Should not be able to get the user information')
-
-  it('Should not be able to upload a video')
-
-  it('Should not be able to remove a video')
-
-  it('Should not be able to rate a video', async function () {
-    const path = '/api/v1/videos/'
-    const data = {
-      rating: 'likes'
-    }
-
-    const options = {
-      url: server.url,
-      path: path + videoId,
-      token: 'wrong token',
-      fields: data,
-      statusCodeExpected: 401
-    }
-    await makePutBodyRequest(options)
-  })
-
-  it('Should be able to login again')
-
-  it('Should have an expired access token')
-
-  it('Should refresh the token')
-
-  it('Should be able to upload a video again')
-
-  it('Should be able to create a new user', async function () {
-    await createUser(server.url, accessToken, 'user_1', 'super password', 2 * 1024 * 1024)
-  })
-
-  it('Should be able to login with this user', async function () {
-    server.user = {
-      username: 'user_1',
-      password: 'super password'
-    }
-
-    accessTokenUser = await loginAndGetAccessToken(server)
-  })
-
-  it('Should be able to get the user information', async function () {
-    const res = await getMyUserInformation(server.url, accessTokenUser)
-    const user = res.body
-
-    expect(user.username)
-      .to
-      .equal('user_1')
-    expect(user.email)
-      .to
-      .equal('user_1@example.com')
-    expect(user.displayNSFW).to.be.false
-    expect(user.videoQuota)
-      .to
-      .equal(2 * 1024 * 1024)
-    expect(user.roleLabel)
-      .to
-      .equal('User')
-    expect(user.id)
-      .to
-      .be
-      .a('number')
-  })
-
-  it('Should be able to upload a video with this user', async function () {
-    this.timeout(5000)
-
-    const videoAttributes = {
-      name: 'super user video'
-    }
-    await uploadVideo(server.url, accessTokenUser, videoAttributes)
-  })
-
-  it('Should be able to list my videos', async function () {
-    const res = await getMyVideos(server.url, accessTokenUser, 0, 5)
-    expect(res.body.total)
-      .to
-      .equal(1)
-
-    const videos = res.body.data
-    expect(videos)
-      .to
-      .have
-      .lengthOf(1)
-
-    expect(videos[ 0 ].name)
-      .to
-      .equal('super user video')
-  })
-
-  it('Should list all the users', async function () {
-    const res = await getUsersList(server.url, server.accessToken)
-    const result = res.body
-    const total = result.total
-    const users = result.data
-
-    expect(total)
-      .to
-      .equal(2)
-    expect(users)
-      .to
-      .be
-      .an('array')
-    expect(users.length)
-      .to
-      .equal(2)
-
-    const user = users[ 0 ]
-    expect(user.username)
-      .to
-      .equal('user_1')
-    expect(user.email)
-      .to
-      .equal('user_1@example.com')
-    expect(user.displayNSFW).to.be.false
-
-    const rootUser = users[ 1 ]
-    expect(rootUser.username)
-      .to
-      .equal('root')
-    expect(rootUser.email)
-      .to
-      .equal('admin1@example.com')
-    expect(rootUser.displayNSFW).to.be.false
-
-    userId = user.id
-  })
-
-  it('Should list only the first user by username asc', async function () {
-    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, 'username')
-
-    const result = res.body
-    const total = result.total
-    const users = result.data
-
-    expect(total)
-      .to
-      .equal(2)
-    expect(users.length)
-      .to
-      .equal(1)
-
-    const user = users[ 0 ]
-    expect(user.username)
-      .to
-      .equal('root')
-    expect(user.email)
-      .to
-      .equal('admin1@example.com')
-    expect(user.roleLabel)
-      .to
-      .equal('Administrator')
-    expect(user.displayNSFW).to.be.false
-  })
-
-  it('Should list only the first user by username desc', async function () {
-    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-username')
-    const result = res.body
-    const total = result.total
-    const users = result.data
-
-    expect(total)
-      .to
-      .equal(2)
-    expect(users.length)
-      .to
-      .equal(1)
-
-    const user = users[ 0 ]
-    expect(user.username)
-      .to
-      .equal('user_1')
-    expect(user.email)
-      .to
-      .equal('user_1@example.com')
-    expect(user.displayNSFW).to.be.false
-  })
-
-  it('Should list only the second user by createdAt desc', async function () {
-    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-createdAt')
-    const result = res.body
-    const total = result.total
-    const users = result.data
-
-    expect(total)
-      .to
-      .equal(2)
-    expect(users.length)
-      .to
-      .equal(1)
-
-    const user = users[ 0 ]
-    expect(user.username)
-      .to
-      .equal('user_1')
-    expect(user.email)
-      .to
-      .equal('user_1@example.com')
-    expect(user.displayNSFW).to.be.false
-  })
-
-  it('Should list all the users by createdAt asc', async function () {
-    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt')
-    const result = res.body
-    const total = result.total
-    const users = result.data
-
-    expect(total)
-      .to
-      .equal(2)
-    expect(users.length)
-      .to
-      .equal(2)
-
-    expect(users[ 0 ].username)
-      .to
-      .equal('root')
-    expect(users[ 0 ].email)
-      .to
-      .equal('admin1@example.com')
-    expect(users[ 0 ].displayNSFW).to.be.false
-
-    expect(users[ 1 ].username)
-      .to
-      .equal('user_1')
-    expect(users[ 1 ].email)
-      .to
-      .equal('user_1@example.com')
-    expect(users[ 1 ].displayNSFW).to.be.false
-  })
-
-  it('Should update my password', async function () {
-    await updateMyUser(server.url, accessTokenUser, 'new password')
-    server.user.password = 'new password'
-
-    await login(server.url, server.client, server.user, 200)
-  })
-
-  it('Should be able to change the NSFW display attribute', async function () {
-    await updateMyUser(server.url, accessTokenUser, undefined, true)
-
-    const res = await getMyUserInformation(server.url, accessTokenUser)
-    const user = res.body
-
-    expect(user.username)
-      .to
-      .equal('user_1')
-    expect(user.email)
-      .to
-      .equal('user_1@example.com')
-    expect(user.displayNSFW).to.be.ok
-    expect(user.videoQuota)
-      .to
-      .equal(2 * 1024 * 1024)
-    expect(user.id)
-      .to
-      .be
-      .a('number')
-  })
-
-  it('Should be able to change the autoPlayVideo attribute', async function () {
-    await updateMyUser(server.url, accessTokenUser, undefined, undefined, undefined, false)
-
-    const res = await getMyUserInformation(server.url, accessTokenUser)
-    const user = res.body
-
-    expect(user.autoPlayVideo).to.be.false
-  })
-
-  it('Should be able to change the email display attribute', async function () {
-    await updateMyUser(server.url, accessTokenUser, undefined, undefined, 'updated@example.com')
-
-    const res = await getMyUserInformation(server.url, accessTokenUser)
-    const user = res.body
-
-    expect(user.username)
-      .to
-      .equal('user_1')
-    expect(user.email)
-      .to
-      .equal('updated@example.com')
-    expect(user.displayNSFW).to.be.ok
-    expect(user.videoQuota)
-      .to
-      .equal(2 * 1024 * 1024)
-    expect(user.id)
-      .to
-      .be
-      .a('number')
-  })
-
-  it('Should be able to update another user', async function () {
-    await updateUser(server.url, userId, accessToken, 'updated2@example.com', 42, UserRole.MODERATOR)
-
-    const res = await getUserInformation(server.url, accessToken, userId)
-    const user = res.body
-
-    expect(user.username)
-      .to
-      .equal('user_1')
-    expect(user.email)
-      .to
-      .equal('updated2@example.com')
-    expect(user.displayNSFW).to.be.ok
-    expect(user.videoQuota)
-      .to
-      .equal(42)
-    expect(user.roleLabel)
-      .to
-      .equal('Moderator')
-    expect(user.id)
-      .to
-      .be
-      .a('number')
-  })
-
-  it('Should not be able to delete a user by a moderator', async function () {
-    await removeUser(server.url, 2, accessTokenUser, 403)
-  })
-
-  it('Should be able to list video blacklist by a moderator', async function () {
-    await getBlacklistedVideosList(server.url, accessTokenUser)
-  })
-
-  it('Should be able to remove this user', async function () {
-    await removeUser(server.url, userId, accessToken)
-  })
-
-  it('Should not be able to login with this user', async function () {
-    // server.user is already set to user 1
-    await login(server.url, server.client, server.user, 400)
-  })
-
-  it('Should not have videos of this user', async function () {
-    const res = await getVideosList(server.url)
-
-    expect(res.body.total)
-      .to
-      .equal(1)
-
-    const video = res.body.data[ 0 ]
-    expect(video.accountName)
-      .to
-      .equal('root')
-  })
-
-  it('Should register a new user', async function () {
-    await registerUser(server.url, 'user_15', 'my super password')
-  })
-
-  it('Should be able to login with this registered user', async function () {
-    server.user = {
-      username: 'user_15',
-      password: 'my super password'
-    }
-
-    accessToken = await loginAndGetAccessToken(server)
-  })
-
-  it('Should have the correct video quota', async function () {
-    const res = await getMyUserInformation(server.url, accessToken)
-    const user = res.body
-
-    expect(user.videoQuota)
-      .to
-      .equal(5 * 1024 * 1024)
-  })
-
-  after(async function () {
-    killallServers([ server ])
-
-    // Keep the logs if the test failed
-    if (this[ 'ok' ]) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
new file mode 100644 (file)
index 0000000..2e3a0b9
--- /dev/null
@@ -0,0 +1,534 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import { UserRole } from '../../../../shared/index'
+import {
+  createUser,
+  flushTests,
+  getBlacklistedVideosList,
+  getMyUserInformation,
+  getUserInformation,
+  getUsersList,
+  getUsersListPaginationAndSort,
+  getUserVideoRating,
+  getVideosList,
+  killallServers,
+  login,
+  loginAndGetAccessToken,
+  makePutBodyRequest,
+  rateVideo,
+  registerUser,
+  removeUser,
+  removeVideo,
+  runServer,
+  ServerInfo,
+  updateMyUser,
+  updateUser,
+  uploadVideo
+} from '../../utils/index'
+import { follow } from '../../utils/server/follows'
+import { getMyVideos } from '../../utils/videos/videos'
+import { setAccessTokensToServers } from '../../utils/users/login'
+
+const expect = chai.expect
+
+describe('Test users', function () {
+  let server: ServerInfo
+  let accessToken: string
+  let accessTokenUser: string
+  let videoId: number
+  let userId: number
+
+  before(async function () {
+    this.timeout(10000)
+
+    await flushTests()
+    server = await runServer(1)
+
+    await setAccessTokensToServers([ server ])
+  })
+
+  it('Should create a new client')
+
+  it('Should return the first client')
+
+  it('Should remove the last client')
+
+  it('Should not login with an invalid client id', async function () {
+    const client = { id: 'client', secret: server.client.secret }
+    const res = await login(server.url, client, server.user, 400)
+
+    expect(res.body.error)
+      .to
+      .equal('invalid_client')
+  })
+
+  it('Should not login with an invalid client secret', async function () {
+    const client = { id: server.client.id, secret: 'coucou' }
+    const res = await login(server.url, client, server.user, 400)
+
+    expect(res.body.error)
+      .to
+      .equal('invalid_client')
+  })
+
+  it('Should not login with an invalid username', async function () {
+    const user = { username: 'captain crochet', password: server.user.password }
+    const res = await login(server.url, server.client, user, 400)
+
+    expect(res.body.error)
+      .to
+      .equal('invalid_grant')
+  })
+
+  it('Should not login with an invalid password', async function () {
+    const user = { username: server.user.username, password: 'mew_three' }
+    const res = await login(server.url, server.client, user, 400)
+
+    expect(res.body.error)
+      .to
+      .equal('invalid_grant')
+  })
+
+  it('Should not be able to upload a video', async function () {
+    accessToken = 'my_super_token'
+
+    const videoAttributes = {}
+    await uploadVideo(server.url, accessToken, videoAttributes, 401)
+  })
+
+  it('Should not be able to follow', async function () {
+    accessToken = 'my_super_token'
+    await follow(server.url, [ 'http://example.com' ], accessToken, 401)
+  })
+
+  it('Should not be able to unfollow')
+
+  it('Should be able to login', async function () {
+    const res = await login(server.url, server.client, server.user, 200)
+
+    accessToken = res.body.access_token
+  })
+
+  it('Should upload the video with the correct token', async function () {
+    const videoAttributes = {}
+    await uploadVideo(server.url, accessToken, videoAttributes)
+    const res = await getVideosList(server.url)
+    const video = res.body.data[ 0 ]
+
+    expect(video.accountName)
+      .to
+      .equal('root')
+    videoId = video.id
+  })
+
+  it('Should upload the video again with the correct token', async function () {
+    const videoAttributes = {}
+    await uploadVideo(server.url, accessToken, videoAttributes)
+  })
+
+  it('Should retrieve a video rating', async function () {
+    await rateVideo(server.url, accessToken, videoId, 'like')
+    const res = await getUserVideoRating(server.url, accessToken, videoId)
+    const rating = res.body
+
+    expect(rating.videoId)
+      .to
+      .equal(videoId)
+    expect(rating.rating)
+      .to
+      .equal('like')
+  })
+
+  it('Should not be able to remove the video with an incorrect token', async function () {
+    await removeVideo(server.url, 'bad_token', videoId, 401)
+  })
+
+  it('Should not be able to remove the video with the token of another account')
+
+  it('Should be able to remove the video with the correct token', async function () {
+    await removeVideo(server.url, accessToken, videoId)
+  })
+
+  it('Should logout (revoke token)')
+
+  it('Should not be able to get the user information')
+
+  it('Should not be able to upload a video')
+
+  it('Should not be able to remove a video')
+
+  it('Should not be able to rate a video', async function () {
+    const path = '/api/v1/videos/'
+    const data = {
+      rating: 'likes'
+    }
+
+    const options = {
+      url: server.url,
+      path: path + videoId,
+      token: 'wrong token',
+      fields: data,
+      statusCodeExpected: 401
+    }
+    await makePutBodyRequest(options)
+  })
+
+  it('Should be able to login again')
+
+  it('Should have an expired access token')
+
+  it('Should refresh the token')
+
+  it('Should be able to upload a video again')
+
+  it('Should be able to create a new user', async function () {
+    await createUser(server.url, accessToken, 'user_1', 'super password', 2 * 1024 * 1024)
+  })
+
+  it('Should be able to login with this user', async function () {
+    server.user = {
+      username: 'user_1',
+      password: 'super password'
+    }
+
+    accessTokenUser = await loginAndGetAccessToken(server)
+  })
+
+  it('Should be able to get the user information', async function () {
+    const res = await getMyUserInformation(server.url, accessTokenUser)
+    const user = res.body
+
+    expect(user.username)
+      .to
+      .equal('user_1')
+    expect(user.email)
+      .to
+      .equal('user_1@example.com')
+    expect(user.displayNSFW).to.be.false
+    expect(user.videoQuota)
+      .to
+      .equal(2 * 1024 * 1024)
+    expect(user.roleLabel)
+      .to
+      .equal('User')
+    expect(user.id)
+      .to
+      .be
+      .a('number')
+  })
+
+  it('Should be able to upload a video with this user', async function () {
+    this.timeout(5000)
+
+    const videoAttributes = {
+      name: 'super user video'
+    }
+    await uploadVideo(server.url, accessTokenUser, videoAttributes)
+  })
+
+  it('Should be able to list my videos', async function () {
+    const res = await getMyVideos(server.url, accessTokenUser, 0, 5)
+    expect(res.body.total)
+      .to
+      .equal(1)
+
+    const videos = res.body.data
+    expect(videos)
+      .to
+      .have
+      .lengthOf(1)
+
+    expect(videos[ 0 ].name)
+      .to
+      .equal('super user video')
+  })
+
+  it('Should list all the users', async function () {
+    const res = await getUsersList(server.url, server.accessToken)
+    const result = res.body
+    const total = result.total
+    const users = result.data
+
+    expect(total)
+      .to
+      .equal(2)
+    expect(users)
+      .to
+      .be
+      .an('array')
+    expect(users.length)
+      .to
+      .equal(2)
+
+    const user = users[ 0 ]
+    expect(user.username)
+      .to
+      .equal('user_1')
+    expect(user.email)
+      .to
+      .equal('user_1@example.com')
+    expect(user.displayNSFW).to.be.false
+
+    const rootUser = users[ 1 ]
+    expect(rootUser.username)
+      .to
+      .equal('root')
+    expect(rootUser.email)
+      .to
+      .equal('admin1@example.com')
+    expect(rootUser.displayNSFW).to.be.false
+
+    userId = user.id
+  })
+
+  it('Should list only the first user by username asc', async function () {
+    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, 'username')
+
+    const result = res.body
+    const total = result.total
+    const users = result.data
+
+    expect(total)
+      .to
+      .equal(2)
+    expect(users.length)
+      .to
+      .equal(1)
+
+    const user = users[ 0 ]
+    expect(user.username)
+      .to
+      .equal('root')
+    expect(user.email)
+      .to
+      .equal('admin1@example.com')
+    expect(user.roleLabel)
+      .to
+      .equal('Administrator')
+    expect(user.displayNSFW).to.be.false
+  })
+
+  it('Should list only the first user by username desc', async function () {
+    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-username')
+    const result = res.body
+    const total = result.total
+    const users = result.data
+
+    expect(total)
+      .to
+      .equal(2)
+    expect(users.length)
+      .to
+      .equal(1)
+
+    const user = users[ 0 ]
+    expect(user.username)
+      .to
+      .equal('user_1')
+    expect(user.email)
+      .to
+      .equal('user_1@example.com')
+    expect(user.displayNSFW).to.be.false
+  })
+
+  it('Should list only the second user by createdAt desc', async function () {
+    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 1, '-createdAt')
+    const result = res.body
+    const total = result.total
+    const users = result.data
+
+    expect(total)
+      .to
+      .equal(2)
+    expect(users.length)
+      .to
+      .equal(1)
+
+    const user = users[ 0 ]
+    expect(user.username)
+      .to
+      .equal('user_1')
+    expect(user.email)
+      .to
+      .equal('user_1@example.com')
+    expect(user.displayNSFW).to.be.false
+  })
+
+  it('Should list all the users by createdAt asc', async function () {
+    const res = await getUsersListPaginationAndSort(server.url, server.accessToken, 0, 2, 'createdAt')
+    const result = res.body
+    const total = result.total
+    const users = result.data
+
+    expect(total)
+      .to
+      .equal(2)
+    expect(users.length)
+      .to
+      .equal(2)
+
+    expect(users[ 0 ].username)
+      .to
+      .equal('root')
+    expect(users[ 0 ].email)
+      .to
+      .equal('admin1@example.com')
+    expect(users[ 0 ].displayNSFW).to.be.false
+
+    expect(users[ 1 ].username)
+      .to
+      .equal('user_1')
+    expect(users[ 1 ].email)
+      .to
+      .equal('user_1@example.com')
+    expect(users[ 1 ].displayNSFW).to.be.false
+  })
+
+  it('Should update my password', async function () {
+    await updateMyUser(server.url, accessTokenUser, 'new password')
+    server.user.password = 'new password'
+
+    await login(server.url, server.client, server.user, 200)
+  })
+
+  it('Should be able to change the NSFW display attribute', async function () {
+    await updateMyUser(server.url, accessTokenUser, undefined, true)
+
+    const res = await getMyUserInformation(server.url, accessTokenUser)
+    const user = res.body
+
+    expect(user.username)
+      .to
+      .equal('user_1')
+    expect(user.email)
+      .to
+      .equal('user_1@example.com')
+    expect(user.displayNSFW).to.be.ok
+    expect(user.videoQuota)
+      .to
+      .equal(2 * 1024 * 1024)
+    expect(user.id)
+      .to
+      .be
+      .a('number')
+  })
+
+  it('Should be able to change the autoPlayVideo attribute', async function () {
+    await updateMyUser(server.url, accessTokenUser, undefined, undefined, undefined, false)
+
+    const res = await getMyUserInformation(server.url, accessTokenUser)
+    const user = res.body
+
+    expect(user.autoPlayVideo).to.be.false
+  })
+
+  it('Should be able to change the email display attribute', async function () {
+    await updateMyUser(server.url, accessTokenUser, undefined, undefined, 'updated@example.com')
+
+    const res = await getMyUserInformation(server.url, accessTokenUser)
+    const user = res.body
+
+    expect(user.username)
+      .to
+      .equal('user_1')
+    expect(user.email)
+      .to
+      .equal('updated@example.com')
+    expect(user.displayNSFW).to.be.ok
+    expect(user.videoQuota)
+      .to
+      .equal(2 * 1024 * 1024)
+    expect(user.id)
+      .to
+      .be
+      .a('number')
+  })
+
+  it('Should be able to update another user', async function () {
+    await updateUser(server.url, userId, accessToken, 'updated2@example.com', 42, UserRole.MODERATOR)
+
+    const res = await getUserInformation(server.url, accessToken, userId)
+    const user = res.body
+
+    expect(user.username)
+      .to
+      .equal('user_1')
+    expect(user.email)
+      .to
+      .equal('updated2@example.com')
+    expect(user.displayNSFW).to.be.ok
+    expect(user.videoQuota)
+      .to
+      .equal(42)
+    expect(user.roleLabel)
+      .to
+      .equal('Moderator')
+    expect(user.id)
+      .to
+      .be
+      .a('number')
+  })
+
+  it('Should not be able to delete a user by a moderator', async function () {
+    await removeUser(server.url, 2, accessTokenUser, 403)
+  })
+
+  it('Should be able to list video blacklist by a moderator', async function () {
+    await getBlacklistedVideosList(server.url, accessTokenUser)
+  })
+
+  it('Should be able to remove this user', async function () {
+    await removeUser(server.url, userId, accessToken)
+  })
+
+  it('Should not be able to login with this user', async function () {
+    // server.user is already set to user 1
+    await login(server.url, server.client, server.user, 400)
+  })
+
+  it('Should not have videos of this user', async function () {
+    const res = await getVideosList(server.url)
+
+    expect(res.body.total)
+      .to
+      .equal(1)
+
+    const video = res.body.data[ 0 ]
+    expect(video.accountName)
+      .to
+      .equal('root')
+  })
+
+  it('Should register a new user', async function () {
+    await registerUser(server.url, 'user_15', 'my super password')
+  })
+
+  it('Should be able to login with this registered user', async function () {
+    server.user = {
+      username: 'user_15',
+      password: 'my super password'
+    }
+
+    accessToken = await loginAndGetAccessToken(server)
+  })
+
+  it('Should have the correct video quota', async function () {
+    const res = await getMyUserInformation(server.url, accessToken)
+    const user = res.body
+
+    expect(user.videoQuota)
+      .to
+      .equal(5 * 1024 * 1024)
+  })
+
+  after(async function () {
+    killallServers([ server ])
+
+    // Keep the logs if the test failed
+    if (this[ 'ok' ]) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/video-abuse.ts b/server/tests/api/video-abuse.ts
deleted file mode 100644 (file)
index 4a0b6b5..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import {
-  flushAndRunMultipleServers,
-  flushTests,
-  getVideoAbusesList,
-  getVideosList,
-  killallServers,
-  reportVideoAbuse,
-  ServerInfo,
-  setAccessTokensToServers,
-  uploadVideo,
-  wait
-} from '../utils'
-import { doubleFollow } from '../utils/follows'
-
-const expect = chai.expect
-
-describe('Test video abuses', function () {
-  let servers: ServerInfo[] = []
-
-  before(async function () {
-    this.timeout(50000)
-
-    // Run servers
-    servers = await flushAndRunMultipleServers(2)
-
-    // Get the access tokens
-    await setAccessTokensToServers(servers)
-
-    // Server 1 and server 2 follow each other
-    await doubleFollow(servers[0], servers[1])
-
-    // Upload some videos on each servers
-    const video1Attributes = {
-      name: 'my super name for server 1',
-      description: 'my super description for server 1'
-    }
-    await uploadVideo(servers[0].url, servers[0].accessToken, video1Attributes)
-
-    const video2Attributes = {
-      name: 'my super name for server 2',
-      description: 'my super description for server 2'
-    }
-    await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes)
-
-    // Wait videos propagation, server 2 has transcoding enabled
-    await wait(15000)
-
-    const res = await getVideosList(servers[0].url)
-    const videos = res.body.data
-
-    expect(videos.length).to.equal(2)
-
-    servers[0].video = videos.find(video => video.name === 'my super name for server 1')
-    servers[1].video = videos.find(video => video.name === 'my super name for server 2')
-  })
-
-  it('Should not have video abuses', async function () {
-    const res = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(0)
-  })
-
-  it('Should report abuse on a local video', async function () {
-    this.timeout(10000)
-
-    const reason = 'my super bad reason'
-    await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[0].video.id, reason)
-
-    // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2
-    await wait(5000)
-  })
-
-  it('Should have 1 video abuses on server 1 and 0 on server 2', async function () {
-    const res1 = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
-
-    expect(res1.body.total).to.equal(1)
-    expect(res1.body.data).to.be.an('array')
-    expect(res1.body.data.length).to.equal(1)
-
-    const abuse = res1.body.data[0]
-    expect(abuse.reason).to.equal('my super bad reason')
-    expect(abuse.reporterUsername).to.equal('root')
-    expect(abuse.reporterServerHost).to.equal('localhost:9001')
-    expect(abuse.videoId).to.equal(servers[0].video.id)
-
-    const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
-    expect(res2.body.total).to.equal(0)
-    expect(res2.body.data).to.be.an('array')
-    expect(res2.body.data.length).to.equal(0)
-  })
-
-  it('Should report abuse on a remote video', async function () {
-    this.timeout(10000)
-
-    const reason = 'my super bad reason 2'
-    await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[1].video.id, reason)
-
-    // We wait requests propagation
-    await wait(5000)
-  })
-
-  it('Should have 2 video abuse on server 1 and 1 on server 2', async function () {
-    const res1 = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
-    expect(res1.body.total).to.equal(2)
-    expect(res1.body.data).to.be.an('array')
-    expect(res1.body.data.length).to.equal(2)
-
-    const abuse1 = res1.body.data[0]
-    expect(abuse1.reason).to.equal('my super bad reason')
-    expect(abuse1.reporterUsername).to.equal('root')
-    expect(abuse1.reporterServerHost).to.equal('localhost:9001')
-    expect(abuse1.videoId).to.equal(servers[0].video.id)
-
-    const abuse2 = res1.body.data[1]
-    expect(abuse2.reason).to.equal('my super bad reason 2')
-    expect(abuse2.reporterUsername).to.equal('root')
-    expect(abuse2.reporterServerHost).to.equal('localhost:9001')
-    expect(abuse2.videoId).to.equal(servers[1].video.id)
-
-    const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
-    expect(res2.body.total).to.equal(1)
-    expect(res2.body.data).to.be.an('array')
-    expect(res2.body.data.length).to.equal(1)
-
-    const abuse3 = res2.body.data[0]
-    expect(abuse3.reason).to.equal('my super bad reason 2')
-    expect(abuse3.reporterUsername).to.equal('root')
-    expect(abuse3.reporterServerHost).to.equal('localhost:9001')
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/video-blacklist-management.ts b/server/tests/api/video-blacklist-management.ts
deleted file mode 100644 (file)
index 0c636e0..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/* tslint:disable:no-unused-expressions */
-
-import * as chai from 'chai'
-import * as lodash from 'lodash'
-import 'mocha'
-import {
-  addVideoToBlacklist,
-  flushAndRunMultipleServers,
-  flushTests,
-  getBlacklistedVideosList,
-  getSortedBlacklistedVideosList,
-  getVideosList,
-  killallServers,
-  removeVideoFromBlacklist,
-  ServerInfo,
-  setAccessTokensToServers,
-  uploadVideo,
-  wait
-} from '../utils'
-import { doubleFollow } from '../utils/follows'
-
-const expect = chai.expect
-const orderBy = lodash.orderBy
-
-describe('Test video blacklist management', function () {
-  let servers: ServerInfo[] = []
-
-  async function blacklistVideosOnServer (server: ServerInfo) {
-    const res = await getVideosList(server.url)
-
-    const videos = res.body.data
-    for (let video of videos) {
-      await addVideoToBlacklist(server.url, server.accessToken, video.id)
-    }
-  }
-
-  before(async function () {
-    this.timeout(50000)
-
-    // Run servers
-    servers = await flushAndRunMultipleServers(2)
-
-    // Get the access tokens
-    await setAccessTokensToServers(servers)
-
-    // Server 1 and server 2 follow each other
-    await doubleFollow(servers[0], servers[1])
-
-    // Upload 2 videos on server 2
-    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' })
-    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' })
-
-    // Wait videos propagation, server 2 has transcoding enabled
-    await wait(15000)
-
-    // Blacklist the two videos on server 1
-    await blacklistVideosOnServer(servers[0])
-  })
-
-  describe('When listing blacklisted videos', function () {
-    it('Should display all the blacklisted videos', async function () {
-      const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken)
-
-      expect(res.body.total).to.equal(2)
-
-      const videos = res.body.data
-      expect(videos).to.be.an('array')
-      expect(videos.length).to.equal(2)
-    })
-
-    it('Should get the correct sort when sorting by descending id', async function () {
-      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id')
-      expect(res.body.total).to.equal(2)
-
-      const videos = res.body.data
-      expect(videos).to.be.an('array')
-      expect(videos.length).to.equal(2)
-
-      const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ])
-
-      expect(videos).to.deep.equal(result)
-    })
-
-    it('Should get the correct sort when sorting by descending video name', async function () {
-      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
-      expect(res.body.total).to.equal(2)
-
-      const videos = res.body.data
-      expect(videos).to.be.an('array')
-      expect(videos.length).to.equal(2)
-
-      const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ])
-
-      expect(videos).to.deep.equal(result)
-    })
-
-    it('Should get the correct sort when sorting by ascending creation date', async function () {
-      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt')
-      expect(res.body.total).to.equal(2)
-
-      const videos = res.body.data
-      expect(videos).to.be.an('array')
-      expect(videos.length).to.equal(2)
-
-      const result = orderBy(res.body.data, [ 'createdAt' ])
-
-      expect(videos).to.deep.equal(result)
-    })
-  })
-
-  describe('When removing a blacklisted video', function () {
-    let videoToRemove
-    let blacklist = []
-
-    it('Should not have any video in videos list on server 1', async function () {
-      const res = await getVideosList(servers[0].url)
-      expect(res.body.total).to.equal(0)
-      expect(res.body.data).to.be.an('array')
-      expect(res.body.data.length).to.equal(0)
-    })
-
-    it('Should remove a video from the blacklist on server 1', async function () {
-      // Get one video in the blacklist
-      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
-      videoToRemove = res.body.data[0]
-      blacklist = res.body.data.slice(1)
-
-      // Remove it
-      await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.videoId)
-    })
-
-    it('Should have the ex-blacklisted video in videos list on server 1', async function () {
-      const res = await getVideosList(servers[0].url)
-      expect(res.body.total).to.equal(1)
-
-      const videos = res.body.data
-      expect(videos).to.be.an('array')
-      expect(videos.length).to.equal(1)
-
-      expect(videos[0].name).to.equal(videoToRemove.name)
-      expect(videos[0].id).to.equal(videoToRemove.videoId)
-    })
-
-    it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () {
-      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
-      expect(res.body.total).to.equal(1)
-
-      const videos = res.body.data
-      expect(videos).to.be.an('array')
-      expect(videos.length).to.equal(1)
-      expect(videos).to.deep.equal(blacklist)
-    })
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/video-blacklist.ts b/server/tests/api/video-blacklist.ts
deleted file mode 100644 (file)
index 3afd8c5..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import {
-  addVideoToBlacklist,
-  flushAndRunMultipleServers,
-  flushTests,
-  getVideosList,
-  killallServers,
-  searchVideo,
-  ServerInfo,
-  setAccessTokensToServers,
-  uploadVideo,
-  wait
-} from '../utils'
-import { doubleFollow } from '../utils/follows'
-
-const expect = chai.expect
-
-describe('Test video blacklists', function () {
-  let servers: ServerInfo[] = []
-
-  before(async function () {
-    this.timeout(50000)
-
-    // Run servers
-    servers = await flushAndRunMultipleServers(2)
-
-    // Get the access tokens
-    await setAccessTokensToServers(servers)
-
-    // Server 1 and server 2 follow each other
-    await doubleFollow(servers[0], servers[1])
-
-    // Upload a video on server 2
-    const videoAttributes = {
-      name: 'my super name for server 2',
-      description: 'my super description for server 2'
-    }
-    await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes)
-
-    // Wait videos propagation, server 2 has transcoding enabled
-    await wait(10000)
-
-    const res = await getVideosList(servers[0].url)
-    const videos = res.body.data
-
-    expect(videos.length).to.equal(1)
-
-    servers[0].remoteVideo = videos.find(video => video.name === 'my super name for server 2')
-  })
-
-  it('Should blacklist a remote video on server 1', async function () {
-    await addVideoToBlacklist(servers[0].url, servers[0].accessToken, servers[0].remoteVideo.id)
-  })
-
-  it('Should not have the video blacklisted in videos list on server 1', async function () {
-    const res = await getVideosList(servers[0].url)
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(0)
-  })
-
-  it('Should not have the video blacklisted in videos search on server 1', async function () {
-    const res = await searchVideo(servers[0].url, 'name')
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(0)
-  })
-
-  it('Should have the blacklisted video in videos list on server 2', async function () {
-    const res = await getVideosList(servers[1].url)
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(1)
-  })
-
-  it('Should have the video blacklisted in videos search on server 2', async function () {
-    const res = await searchVideo(servers[1].url, 'name')
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data.length).to.equal(1)
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/video-channels.ts b/server/tests/api/video-channels.ts
deleted file mode 100644 (file)
index 2d8efb3..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import 'mocha'
-import * as chai from 'chai'
-const expect = chai.expect
-
-import {
-  ServerInfo,
-  flushTests,
-  runServer,
-  setAccessTokensToServers,
-  killallServers,
-  getMyUserInformation,
-  getVideoChannelsList,
-  addVideoChannel,
-  getAccountVideoChannelsList,
-  updateVideoChannel,
-  deleteVideoChannel,
-  getVideoChannel
-} from '../utils'
-import { User } from '../../../shared'
-
-describe('Test a video channels', function () {
-  let server: ServerInfo
-  let userInfo: User
-  let videoChannelId: number
-
-  before(async function () {
-    this.timeout(10000)
-
-    await flushTests()
-
-    server = await runServer(1)
-
-    await setAccessTokensToServers([ server ])
-  })
-
-  it('Should have one video channel (created with root)', async () => {
-    const res = await getVideoChannelsList(server.url, 0, 2)
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(1)
-  })
-
-  it('Should create another video channel', async () => {
-    const videoChannel = {
-      name: 'second video channel',
-      description: 'super video channel description'
-    }
-    await addVideoChannel(server.url, server.accessToken, videoChannel)
-  })
-
-  it('Should have two video channels when getting my information', async () => {
-    const res = await getMyUserInformation(server.url, server.accessToken)
-    userInfo = res.body
-
-    expect(userInfo.videoChannels).to.be.an('array')
-    expect(userInfo.videoChannels).to.have.lengthOf(2)
-
-    const videoChannels = userInfo.videoChannels
-    expect(videoChannels[0].name).to.equal('Default root channel')
-    expect(videoChannels[1].name).to.equal('second video channel')
-    expect(videoChannels[1].description).to.equal('super video channel description')
-  })
-
-  it('Should have two video channels when getting account channels', async () => {
-    const res = await getAccountVideoChannelsList(server.url, userInfo.account.uuid)
-
-    expect(res.body.total).to.equal(2)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(2)
-
-    const videoChannels = res.body.data
-    expect(videoChannels[0].name).to.equal('Default root channel')
-    expect(videoChannels[1].name).to.equal('second video channel')
-    expect(videoChannels[1].description).to.equal('super video channel description')
-
-    videoChannelId = videoChannels[1].id
-  })
-
-  it('Should list video channels', async () => {
-    const res = await getVideoChannelsList(server.url, 1, 1, '-name')
-
-    expect(res.body.total).to.equal(2)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(1)
-    expect(res.body.data[0].name).to.equal('Default root channel')
-  })
-
-  it('Should update video channel', async () => {
-    const videoChannelAttributes = {
-      name: 'video channel updated',
-      description: 'video channel description updated'
-    }
-
-    await updateVideoChannel(server.url, server.accessToken, videoChannelId, videoChannelAttributes)
-  })
-
-  it('Should have video channel updated', async () => {
-    const res = await getVideoChannelsList(server.url, 0, 1, '-name')
-
-    expect(res.body.total).to.equal(2)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(1)
-    expect(res.body.data[0].name).to.equal('video channel updated')
-    expect(res.body.data[0].description).to.equal('video channel description updated')
-  })
-
-  it('Should get video channel', async () => {
-    const res = await getVideoChannel(server.url, videoChannelId)
-
-    const videoChannel = res.body
-    expect(videoChannel.name).to.equal('video channel updated')
-    expect(videoChannel.description).to.equal('video channel description updated')
-  })
-
-  it('Should delete video channel', async () => {
-    await deleteVideoChannel(server.url, server.accessToken, videoChannelId)
-  })
-
-  it('Should have video channel deleted', async () => {
-    const res = await getVideoChannelsList(server.url, 0, 10)
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(1)
-    expect(res.body.data[0].name).to.equal('Default root channel')
-  })
-
-  after(async function () {
-    killallServers([ server ])
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/video-comments.ts b/server/tests/api/video-comments.ts
deleted file mode 100644 (file)
index f05ca5e..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import { VideoComment, VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model'
-import { dateIsValid, flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, uploadVideo } from '../utils'
-import { addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads, getVideoThreadComments } from '../utils/video-comments'
-
-const expect = chai.expect
-
-describe('Test video comments', function () {
-  let server: ServerInfo
-  let videoId
-  let videoUUID
-  let threadId
-
-  before(async function () {
-    this.timeout(10000)
-
-    await flushTests()
-
-    server = await runServer(1)
-
-    await setAccessTokensToServers([ server ])
-
-    const res = await uploadVideo(server.url, server.accessToken, {})
-    videoUUID = res.body.video.uuid
-    videoId = res.body.video.id
-  })
-
-  it('Should not have threads on this video', async function () {
-    const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(0)
-  })
-
-  it('Should create a thread in this video', async function () {
-    const text = 'my super first comment'
-
-    const res = await addVideoCommentThread(server.url, server.accessToken, videoUUID, text)
-    const comment = res.body.comment
-
-    expect(comment.inReplyToCommentId).to.be.null
-    expect(comment.text).equal('my super first comment')
-    expect(comment.videoId).to.equal(videoId)
-    expect(comment.id).to.equal(comment.threadId)
-    expect(comment.account.name).to.equal('root')
-    expect(comment.account.host).to.equal('localhost:9001')
-    expect(comment.totalReplies).to.equal(0)
-    expect(dateIsValid(comment.createdAt as string)).to.be.true
-    expect(dateIsValid(comment.updatedAt as string)).to.be.true
-  })
-
-  it('Should list threads of this video', async function () {
-    const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(1)
-
-    const comment: VideoComment = res.body.data[0]
-    expect(comment.inReplyToCommentId).to.be.null
-    expect(comment.text).equal('my super first comment')
-    expect(comment.videoId).to.equal(videoId)
-    expect(comment.id).to.equal(comment.threadId)
-    expect(comment.account.name).to.equal('root')
-    expect(comment.account.host).to.equal('localhost:9001')
-    expect(comment.totalReplies).to.equal(0)
-    expect(dateIsValid(comment.createdAt as string)).to.be.true
-    expect(dateIsValid(comment.updatedAt as string)).to.be.true
-
-    threadId = comment.threadId
-  })
-
-  it('Should get all the thread created', async function () {
-    const res = await getVideoThreadComments(server.url, videoUUID, threadId)
-
-    const rootComment = res.body.comment
-    expect(rootComment.inReplyToCommentId).to.be.null
-    expect(rootComment.text).equal('my super first comment')
-    expect(rootComment.videoId).to.equal(videoId)
-    expect(dateIsValid(rootComment.createdAt as string)).to.be.true
-    expect(dateIsValid(rootComment.updatedAt as string)).to.be.true
-  })
-
-  it('Should create multiple replies in this thread', async function () {
-    const text1 = 'my super answer to thread 1'
-    const childCommentRes = await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text1)
-    const childCommentId = childCommentRes.body.comment.id
-
-    const text2 = 'my super answer to answer of thread 1'
-    await addVideoCommentReply(server.url, server.accessToken, videoId, childCommentId, text2)
-
-    const text3 = 'my second answer to thread 1'
-    await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text3)
-  })
-
-  it('Should get correctly the replies', async function () {
-    const res = await getVideoThreadComments(server.url, videoUUID, threadId)
-
-    const tree: VideoCommentThreadTree = res.body
-    expect(tree.comment.text).equal('my super first comment')
-    expect(tree.children).to.have.lengthOf(2)
-
-    const firstChild = tree.children[0]
-    expect(firstChild.comment.text).to.equal('my super answer to thread 1')
-    expect(firstChild.children).to.have.lengthOf(1)
-
-    const childOfFirstChild = firstChild.children[0]
-    expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
-    expect(childOfFirstChild.children).to.have.lengthOf(0)
-
-    const secondChild = tree.children[1]
-    expect(secondChild.comment.text).to.equal('my second answer to thread 1')
-    expect(secondChild.children).to.have.lengthOf(0)
-  })
-
-  it('Should create other threads', async function () {
-    const text1 = 'super thread 2'
-    await addVideoCommentThread(server.url, server.accessToken, videoUUID, text1)
-
-    const text2 = 'super thread 3'
-    await addVideoCommentThread(server.url, server.accessToken, videoUUID, text2)
-  })
-
-  it('Should list the threads', async function () {
-    const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt')
-
-    expect(res.body.total).to.equal(3)
-    expect(res.body.data).to.be.an('array')
-    expect(res.body.data).to.have.lengthOf(3)
-
-    expect(res.body.data[0].text).to.equal('my super first comment')
-    expect(res.body.data[0].totalReplies).to.equal(3)
-    expect(res.body.data[1].text).to.equal('super thread 2')
-    expect(res.body.data[1].totalReplies).to.equal(0)
-    expect(res.body.data[2].text).to.equal('super thread 3')
-    expect(res.body.data[2].totalReplies).to.equal(0)
-  })
-
-  after(async function () {
-    killallServers([ server ])
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/video-description.ts b/server/tests/api/video-description.ts
deleted file mode 100644 (file)
index 7ad1c63..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import {
-  flushAndRunMultipleServers,
-  flushTests,
-  getVideo,
-  getVideoDescription,
-  getVideosList,
-  killallServers,
-  ServerInfo,
-  setAccessTokensToServers,
-  updateVideo,
-  uploadVideo,
-  wait
-} from '../utils'
-import { doubleFollow } from '../utils/follows'
-
-const expect = chai.expect
-
-describe('Test video description', function () {
-  let servers: ServerInfo[] = []
-  let videoUUID = ''
-  let videoId: number
-  let longDescription = 'my super description for server 1'.repeat(50)
-
-  before(async function () {
-    this.timeout(40000)
-
-    // Run servers
-    servers = await flushAndRunMultipleServers(2)
-
-    // Get the access tokens
-    await setAccessTokensToServers(servers)
-
-    // Server 1 and server 2 follow each other
-    await doubleFollow(servers[0], servers[1])
-  })
-
-  it('Should upload video with long description', async function () {
-    this.timeout(10000)
-
-    const attributes = {
-      description: longDescription
-    }
-    await uploadVideo(servers[0].url, servers[0].accessToken, attributes)
-
-    await wait(5000)
-
-    const res = await getVideosList(servers[0].url)
-
-    videoId = res.body.data[0].id
-    videoUUID = res.body.data[0].uuid
-  })
-
-  it('Should have a truncated description on each server', async function () {
-    for (const server of servers) {
-      const res = await getVideo(server.url, videoUUID)
-      const video = res.body
-
-      // 30 characters * 6 -> 240 characters
-      const truncatedDescription = 'my super description for server 1'.repeat(7) +
-                                   'my super descrip...'
-
-      expect(video.description).to.equal(truncatedDescription)
-    }
-  })
-
-  it('Should fetch long description on each server', async function () {
-    for (const server of servers) {
-      const res = await getVideo(server.url, videoUUID)
-      const video = res.body
-
-      const res2 = await getVideoDescription(server.url, video.descriptionPath)
-      expect(res2.body.description).to.equal(longDescription)
-    }
-  })
-
-  it('Should update with a short description', async function () {
-    this.timeout(10000)
-
-    const attributes = {
-      description: 'short description'
-    }
-    await updateVideo(servers[0].url, servers[0].accessToken, videoId, attributes)
-
-    await wait(5000)
-  })
-
-  it('Should have a small description on each server', async function () {
-    for (const server of servers) {
-      const res = await getVideo(server.url, videoUUID)
-      const video = res.body
-
-      expect(video.description).to.equal('short description')
-
-      const res2 = await getVideoDescription(server.url, video.descriptionPath)
-      expect(res2.body.description).to.equal('short description')
-    }
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/video-privacy.ts b/server/tests/api/video-privacy.ts
deleted file mode 100644 (file)
index 26847ef..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import 'mocha'
-import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
-import {
-  flushAndRunMultipleServers,
-  flushTests,
-  getVideosList,
-  killallServers,
-  ServerInfo,
-  setAccessTokensToServers,
-  uploadVideo,
-  wait
-} from '../utils'
-import { doubleFollow } from '../utils/follows'
-import { getUserAccessToken } from '../utils/login'
-import { createUser } from '../utils/users'
-import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../utils/videos'
-
-const expect = chai.expect
-
-describe('Test video privacy', function () {
-  let servers: ServerInfo[] = []
-  let privateVideoId
-  let privateVideoUUID
-  let unlistedVideoUUID
-
-  before(async function () {
-    this.timeout(50000)
-
-    // Run servers
-    servers = await flushAndRunMultipleServers(2)
-
-    // Get the access tokens
-    await setAccessTokensToServers(servers)
-
-    // Server 1 and server 2 follow each other
-    await doubleFollow(servers[0], servers[1])
-  })
-
-  it('Should upload a private video on server 1', async function () {
-    this.timeout(10000)
-
-    const attributes = {
-      privacy: VideoPrivacy.PRIVATE
-    }
-    await uploadVideo(servers[0].url, servers[0].accessToken, attributes)
-
-    await wait(5000)
-  })
-
-  it('Should not have this private video on server 2', async function () {
-    const res = await getVideosList(servers[1].url)
-
-    expect(res.body.total).to.equal(0)
-    expect(res.body.data).to.have.lengthOf(0)
-  })
-
-  it('Should list my (private) videos', async function () {
-    const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 1)
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.have.lengthOf(1)
-
-    privateVideoId = res.body.data[0].id
-    privateVideoUUID = res.body.data[0].uuid
-  })
-
-  it('Should not be able to watch this video with non authenticated user', async function () {
-    await getVideo(servers[0].url, privateVideoUUID, 401)
-  })
-
-  it('Should not be able to watch this private video with another user', async function () {
-    const user = {
-      username: 'hello',
-      password: 'super password'
-    }
-    await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
-
-    const token = await getUserAccessToken(servers[0], user)
-    await getVideoWithToken(servers[0].url, token, privateVideoUUID, 403)
-  })
-
-  it('Should be able to watch this video with the correct user', async function () {
-    await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID)
-  })
-
-  it('Should upload an unlisted video on server 2', async function () {
-    this.timeout(30000)
-
-    const attributes = {
-      name: 'unlisted video',
-      privacy: VideoPrivacy.UNLISTED
-    }
-    await uploadVideo(servers[1].url, servers[1].accessToken, attributes)
-
-    // Server 2 has transcoding enabled
-    await wait(10000)
-  })
-
-  it('Should not have this unlisted video listed on server 1 and 2', async function () {
-    for (const server of servers) {
-      const res = await getVideosList(server.url)
-
-      expect(res.body.total).to.equal(0)
-      expect(res.body.data).to.have.lengthOf(0)
-    }
-  })
-
-  it('Should list my (unlisted) videos', async function () {
-    const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 1)
-
-    expect(res.body.total).to.equal(1)
-    expect(res.body.data).to.have.lengthOf(1)
-
-    unlistedVideoUUID = res.body.data[0].uuid
-  })
-
-  it('Should be able to get this unlisted video', async function () {
-    for (const server of servers) {
-      const res = await getVideo(server.url, unlistedVideoUUID)
-
-      expect(res.body.name).to.equal('unlisted video')
-    }
-  })
-
-  it('Should update the private video to public on server 1', async function () {
-    this.timeout(10000)
-
-    const attribute = {
-      name: 'super video public',
-      privacy: VideoPrivacy.PUBLIC
-    }
-
-    await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute)
-
-    await wait(5000)
-  })
-
-  it('Should have this new public video listed on server 1 and 2', async function () {
-    for (const server of servers) {
-      const res = await getVideosList(server.url)
-
-      expect(res.body.total).to.equal(1)
-      expect(res.body.data).to.have.lengthOf(1)
-      expect(res.body.data[0].name).to.equal('super video public')
-    }
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/video-transcoder.ts b/server/tests/api/video-transcoder.ts
deleted file mode 100644 (file)
index 9a6e91b..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/* tslint:disable:no-unused-expression */
-
-import 'mocha'
-import * as chai from 'chai'
-const expect = chai.expect
-
-import {
-  ServerInfo,
-  flushTests,
-  uploadVideo,
-  getVideosList,
-  wait,
-  setAccessTokensToServers,
-  flushAndRunMultipleServers,
-  killallServers,
-  webtorrentAdd,
-  getVideo
-} from '../utils'
-
-describe('Test video transcoding', function () {
-  let servers: ServerInfo[] = []
-
-  before(async function () {
-    this.timeout(10000)
-
-    // Run servers
-    servers = await flushAndRunMultipleServers(2)
-
-    await setAccessTokensToServers(servers)
-  })
-
-  it('Should not transcode video on server 1', async function () {
-    this.timeout(60000)
-
-    const videoAttributes = {
-      name: 'my super name for server 1',
-      description: 'my super description for server 1',
-      fixture: 'video_short.webm'
-    }
-    await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
-
-    await wait(10000)
-
-    const res = await getVideosList(servers[0].url)
-    const video = res.body.data[0]
-
-    const res2 = await getVideo(servers[0].url, video.id)
-    const videoDetails = res2.body
-    expect(videoDetails.files).to.have.lengthOf(1)
-
-    const magnetUri = videoDetails.files[0].magnetUri
-    expect(magnetUri).to.match(/\.webm/)
-
-    const torrent = await webtorrentAdd(magnetUri)
-    expect(torrent.files).to.be.an('array')
-    expect(torrent.files.length).to.equal(1)
-    expect(torrent.files[0].path).match(/\.webm$/)
-  })
-
-  it('Should transcode video on server 2', async function () {
-    this.timeout(60000)
-
-    const videoAttributes = {
-      name: 'my super name for server 2',
-      description: 'my super description for server 2',
-      fixture: 'video_short.webm'
-    }
-    await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes)
-
-    await wait(20000)
-
-    const res = await getVideosList(servers[1].url)
-
-    const video = res.body.data[0]
-    const res2 = await getVideo(servers[1].url, video.id)
-    const videoDetails = res2.body
-
-    expect(videoDetails.files).to.have.lengthOf(4)
-
-    const magnetUri = videoDetails.files[0].magnetUri
-    expect(magnetUri).to.match(/\.mp4/)
-
-    const torrent = await webtorrentAdd(magnetUri)
-    expect(torrent.files).to.be.an('array')
-    expect(torrent.files.length).to.equal(1)
-    expect(torrent.files[0].path).match(/\.mp4$/)
-  })
-
-  after(async function () {
-    killallServers(servers)
-
-    // Keep the logs if the test failed
-    if (this['ok']) {
-      await flushTests()
-    }
-  })
-})
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
new file mode 100644 (file)
index 0000000..84f730a
--- /dev/null
@@ -0,0 +1,861 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import { join } from 'path'
+import * as request from 'supertest'
+import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
+
+import {
+  addVideoChannel, dateIsValid, doubleFollow, flushAndRunMultipleServers, flushTests, getUserAccessToken, getVideo,
+  getVideoChannelsList, getVideosList, killallServers, rateVideo, removeVideo, ServerInfo, setAccessTokensToServers, testVideoImage,
+  updateVideo, uploadVideo, wait, webtorrentAdd
+} from '../../utils/index'
+import { createUser } from '../../utils/users/users'
+import {
+  addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads,
+  getVideoThreadComments
+} from '../../utils/videos/video-comments'
+import { viewVideo } from '../../utils/videos/videos'
+
+const expect = chai.expect
+
+describe('Test multiple servers', function () {
+  let servers: ServerInfo[] = []
+  const toRemove = []
+  let videoUUID = ''
+  let videoChannelId: number
+
+  before(async function () {
+    this.timeout(120000)
+
+    servers = await flushAndRunMultipleServers(3)
+
+    // Get the access tokens
+    await setAccessTokensToServers(servers)
+
+    const videoChannel = {
+      name: 'my channel',
+      description: 'super channel'
+    }
+    await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel)
+    const channelRes = await getVideoChannelsList(servers[0].url, 0, 1)
+    videoChannelId = channelRes.body.data[0].id
+
+    // Server 1 and server 2 follow each other
+    await doubleFollow(servers[0], servers[1])
+    // Server 1 and server 3 follow each other
+    await doubleFollow(servers[0], servers[2])
+    // Server 2 and server 3 follow each other
+    await doubleFollow(servers[1], servers[2])
+  })
+
+  it('Should not have videos for all servers', async function () {
+    for (const server of servers) {
+      const res = await getVideosList(server.url)
+      const videos = res.body.data
+      expect(videos).to.be.an('array')
+      expect(videos.length).to.equal(0)
+    }
+  })
+
+  describe('Should upload the video and propagate on each server', function () {
+    it('Should upload the video on server 1 and propagate on each server', async function () {
+      this.timeout(25000)
+
+      const videoAttributes = {
+        name: 'my super name for server 1',
+        category: 5,
+        licence: 4,
+        language: 9,
+        nsfw: true,
+        description: 'my super description for server 1',
+        tags: [ 'tag1p1', 'tag2p1' ],
+        channelId: videoChannelId,
+        fixture: 'video_short1.webm'
+      }
+      await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
+
+      await wait(10000)
+
+      // All servers should have this video
+      for (const server of servers) {
+        let baseMagnet = null
+
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+        expect(videos).to.be.an('array')
+        expect(videos.length).to.equal(1)
+        const video = videos[0]
+        expect(video.name).to.equal('my super name for server 1')
+        expect(video.category).to.equal(5)
+        expect(video.categoryLabel).to.equal('Sports')
+        expect(video.licence).to.equal(4)
+        expect(video.licenceLabel).to.equal('Attribution - Non Commercial')
+        expect(video.language).to.equal(9)
+        expect(video.languageLabel).to.equal('Japanese')
+        expect(video.nsfw).to.be.ok
+        expect(video.description).to.equal('my super description for server 1')
+        expect(video.serverHost).to.equal('localhost:9001')
+        expect(video.duration).to.equal(10)
+        expect(dateIsValid(video.createdAt)).to.be.true
+        expect(dateIsValid(video.updatedAt)).to.be.true
+        expect(video.accountName).to.equal('root')
+
+        const res2 = await getVideo(server.url, video.uuid)
+        const videoDetails = res2.body
+
+        expect(videoDetails.channel.name).to.equal('my channel')
+        expect(videoDetails.channel.description).to.equal('super channel')
+        expect(videoDetails.account.name).to.equal('root')
+        expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
+        expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
+        expect(videoDetails.files).to.have.lengthOf(1)
+        expect(videoDetails.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ])
+
+        const file = videoDetails.files[0]
+        const magnetUri = file.magnetUri
+        expect(file.magnetUri).to.have.lengthOf.above(2)
+        expect(file.torrentUrl).to
+          .equal(`http://${videoDetails.serverHost}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
+        expect(file.fileUrl).to.equal(`http://${videoDetails.serverHost}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`)
+        expect(file.resolution).to.equal(720)
+        expect(file.resolutionLabel).to.equal('720p')
+        expect(file.size).to.equal(572456)
+
+        if (server.url !== 'http://localhost:9001') {
+          expect(video.isLocal).to.be.false
+          expect(videoDetails.channel.isLocal).to.be.false
+        } else {
+          expect(video.isLocal).to.be.true
+          expect(videoDetails.channel.isLocal).to.be.true
+        }
+
+        // All servers should have the same magnet Uri
+        if (baseMagnet === null) {
+          baseMagnet = magnetUri
+        } else {
+          expect(baseMagnet).to.equal(magnetUri)
+        }
+
+        const test = await testVideoImage(server.url, 'video_short1.webm', video.thumbnailPath)
+        expect(test).to.equal(true)
+      }
+    })
+
+    it('Should upload the video on server 2 and propagate on each server', async function () {
+      this.timeout(50000)
+
+      const user = {
+        username: 'user1',
+        password: 'super_password'
+      }
+      await createUser(servers[1].url, servers[1].accessToken, user.username, user.password)
+      const userAccessToken = await getUserAccessToken(servers[1], user)
+
+      const videoAttributes = {
+        name: 'my super name for server 2',
+        category: 4,
+        licence: 3,
+        language: 11,
+        nsfw: true,
+        description: 'my super description for server 2',
+        tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
+        fixture: 'video_short2.webm'
+      }
+      await uploadVideo(servers[1].url, userAccessToken, videoAttributes)
+
+      // Transcoding
+      await wait(30000)
+
+      // All servers should have this video
+      for (const server of servers) {
+        let baseMagnet = {}
+
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+        expect(videos).to.be.an('array')
+        expect(videos.length).to.equal(2)
+        const video = videos[1]
+        expect(video.name).to.equal('my super name for server 2')
+        expect(video.category).to.equal(4)
+        expect(video.categoryLabel).to.equal('Art')
+        expect(video.licence).to.equal(3)
+        expect(video.licenceLabel).to.equal('Attribution - No Derivatives')
+        expect(video.language).to.equal(11)
+        expect(video.languageLabel).to.equal('German')
+        expect(video.nsfw).to.be.true
+        expect(video.description).to.equal('my super description for server 2')
+        expect(video.serverHost).to.equal('localhost:9002')
+        expect(video.duration).to.equal(5)
+        expect(dateIsValid(video.createdAt)).to.be.true
+        expect(dateIsValid(video.updatedAt)).to.be.true
+        expect(video.accountName).to.equal('user1')
+
+        if (server.url !== 'http://localhost:9002') {
+          expect(video.isLocal).to.be.false
+        } else {
+          expect(video.isLocal).to.be.true
+        }
+
+        const res2 = await getVideo(server.url, video.uuid)
+        const videoDetails = res2.body
+
+        expect(videoDetails.channel.name).to.equal('Default user1 channel')
+        expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
+        expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
+        expect(videoDetails.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ])
+
+        expect(videoDetails.files).to.have.lengthOf(4)
+
+        // Check common attributes
+        for (const file of videoDetails.files) {
+          expect(file.magnetUri).to.have.lengthOf.above(2)
+
+          // All servers should have the same magnet Uri
+          if (baseMagnet[file.resolution] === undefined) {
+            baseMagnet[file.resolution] = file.magnet
+          } else {
+            expect(baseMagnet[file.resolution]).to.equal(file.magnet)
+          }
+        }
+
+        const file240p = videoDetails.files.find(f => f.resolution === 240)
+        expect(file240p).not.to.be.undefined
+        expect(file240p.resolutionLabel).to.equal('240p')
+        expect(file240p.size).to.be.above(180000).and.below(200000)
+
+        const file360p = videoDetails.files.find(f => f.resolution === 360)
+        expect(file360p).not.to.be.undefined
+        expect(file360p.resolutionLabel).to.equal('360p')
+        expect(file360p.size).to.be.above(270000).and.below(290000)
+
+        const file480p = videoDetails.files.find(f => f.resolution === 480)
+        expect(file480p).not.to.be.undefined
+        expect(file480p.resolutionLabel).to.equal('480p')
+        expect(file480p.size).to.be.above(380000).and.below(400000)
+
+        const file720p = videoDetails.files.find(f => f.resolution === 720)
+        expect(file720p).not.to.be.undefined
+        expect(file720p.resolutionLabel).to.equal('720p')
+        expect(file720p.size).to.be.above(700000).and.below(7200000)
+
+        const test = await testVideoImage(server.url, 'video_short2.webm', videoDetails.thumbnailPath)
+        expect(test).to.equal(true)
+      }
+    })
+
+    it('Should upload two videos on server 3 and propagate on each server', async function () {
+      this.timeout(45000)
+
+      const videoAttributes1 = {
+        name: 'my super name for server 3',
+        category: 6,
+        licence: 5,
+        language: 11,
+        nsfw: true,
+        description: 'my super description for server 3',
+        tags: [ 'tag1p3' ],
+        fixture: 'video_short3.webm'
+      }
+      await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1)
+
+      const videoAttributes2 = {
+        name: 'my super name for server 3-2',
+        category: 7,
+        licence: 6,
+        language: 12,
+        nsfw: false,
+        description: 'my super description for server 3-2',
+        tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
+        fixture: 'video_short.webm'
+      }
+      await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes2)
+
+      await wait(10000)
+
+      let baseMagnet = null
+      // All servers should have this video
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+        expect(videos).to.be.an('array')
+        expect(videos.length).to.equal(4)
+
+        // We not sure about the order of the two last uploads
+        let video1 = null
+        let video2 = null
+        if (videos[2].name === 'my super name for server 3') {
+          video1 = videos[2]
+          video2 = videos[3]
+        } else {
+          video1 = videos[3]
+          video2 = videos[2]
+        }
+
+        expect(video1.name).to.equal('my super name for server 3')
+        expect(video1.category).to.equal(6)
+        expect(video1.categoryLabel).to.equal('Travels')
+        expect(video1.licence).to.equal(5)
+        expect(video1.licenceLabel).to.equal('Attribution - Non Commercial - Share Alike')
+        expect(video1.language).to.equal(11)
+        expect(video1.languageLabel).to.equal('German')
+        expect(video1.nsfw).to.be.ok
+        expect(video1.description).to.equal('my super description for server 3')
+        expect(video1.serverHost).to.equal('localhost:9003')
+        expect(video1.duration).to.equal(5)
+        expect(video1.accountName).to.equal('root')
+        expect(dateIsValid(video1.createdAt)).to.be.true
+        expect(dateIsValid(video1.updatedAt)).to.be.true
+
+        const res2 = await getVideo(server.url, video1.id)
+        const video1Details = res2.body
+        expect(video1Details.files).to.have.lengthOf(1)
+        expect(video1Details.tags).to.deep.equal([ 'tag1p3' ])
+
+        const file1 = video1Details.files[0]
+        expect(file1.magnetUri).to.have.lengthOf.above(2)
+        expect(file1.resolution).to.equal(720)
+        expect(file1.resolutionLabel).to.equal('720p')
+        expect(file1.size).to.equal(292677)
+
+        expect(video2.name).to.equal('my super name for server 3-2')
+        expect(video2.category).to.equal(7)
+        expect(video2.categoryLabel).to.equal('Gaming')
+        expect(video2.licence).to.equal(6)
+        expect(video2.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
+        expect(video2.language).to.equal(12)
+        expect(video2.languageLabel).to.equal('Korean')
+        expect(video2.nsfw).to.be.false
+        expect(video2.description).to.equal('my super description for server 3-2')
+        expect(video2.serverHost).to.equal('localhost:9003')
+        expect(video2.duration).to.equal(5)
+        expect(video2.accountName).to.equal('root')
+        expect(dateIsValid(video2.createdAt)).to.be.true
+        expect(dateIsValid(video2.updatedAt)).to.be.true
+
+        const res3 = await getVideo(server.url, video2.id)
+        const video2Details = res3.body
+        expect(video2Details.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
+
+        expect(video2Details.files).to.have.lengthOf(1)
+
+        const file2 = video2Details.files[0]
+        const magnetUri2 = file2.magnetUri
+        expect(file2.magnetUri).to.have.lengthOf.above(2)
+        expect(file2.resolution).to.equal(720)
+        expect(file2.resolutionLabel).to.equal('720p')
+        expect(file2.size).to.equal(218910)
+
+        if (server.url !== 'http://localhost:9003') {
+          expect(video1.isLocal).to.be.false
+          expect(video2.isLocal).to.be.false
+        } else {
+          expect(video1.isLocal).to.be.true
+          expect(video2.isLocal).to.be.true
+        }
+
+        // All servers should have the same magnet Uri
+        if (baseMagnet === null) {
+          baseMagnet = magnetUri2
+        } else {
+          expect(baseMagnet).to.equal(magnetUri2)
+        }
+
+        const test1 = await testVideoImage(server.url, 'video_short3.webm', video1.thumbnailPath)
+        expect(test1).to.equal(true)
+
+        const test2 = await testVideoImage(server.url, 'video_short.webm', video2.thumbnailPath)
+        expect(test2).to.equal(true)
+      }
+    })
+  })
+
+  describe('Should seed the uploaded video', function () {
+    it('Should add the file 1 by asking server 3', async function () {
+      this.timeout(10000)
+
+      const res = await getVideosList(servers[2].url)
+
+      const video = res.body.data[0]
+      toRemove.push(res.body.data[2])
+      toRemove.push(res.body.data[3])
+
+      const res2 = await getVideo(servers[2].url, video.id)
+      const videoDetails = res2.body
+
+      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
+      expect(torrent.files).to.be.an('array')
+      expect(torrent.files.length).to.equal(1)
+      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+    })
+
+    it('Should add the file 2 by asking server 1', async function () {
+      this.timeout(10000)
+
+      const res = await getVideosList(servers[0].url)
+
+      const video = res.body.data[1]
+      const res2 = await getVideo(servers[0].url, video.id)
+      const videoDetails = res2.body
+
+      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
+      expect(torrent.files).to.be.an('array')
+      expect(torrent.files.length).to.equal(1)
+      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+    })
+
+    it('Should add the file 3 by asking server 2', async function () {
+      this.timeout(10000)
+
+      const res = await getVideosList(servers[1].url)
+
+      const video = res.body.data[2]
+      const res2 = await getVideo(servers[1].url, video.id)
+      const videoDetails = res2.body
+
+      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
+      expect(torrent.files).to.be.an('array')
+      expect(torrent.files.length).to.equal(1)
+      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+    })
+
+    it('Should add the file 3-2 by asking server 1', async function () {
+      this.timeout(10000)
+
+      const res = await getVideosList(servers[0].url)
+
+      const video = res.body.data[3]
+      const res2 = await getVideo(servers[0].url, video.id)
+      const videoDetails = res2.body
+
+      const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
+      expect(torrent.files).to.be.an('array')
+      expect(torrent.files.length).to.equal(1)
+      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+    })
+
+    it('Should add the file 2 in 360p by asking server 1', async function () {
+      this.timeout(10000)
+
+      const res = await getVideosList(servers[0].url)
+
+      const video = res.body.data.find(v => v.name === 'my super name for server 2')
+      const res2 = await getVideo(servers[0].url, video.id)
+      const videoDetails = res2.body
+
+      const file = videoDetails.files.find(f => f.resolution === 360)
+      expect(file).not.to.be.undefined
+
+      const torrent = await webtorrentAdd(file.magnetUri)
+      expect(torrent.files).to.be.an('array')
+      expect(torrent.files.length).to.equal(1)
+      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+    })
+  })
+
+  describe('Should update video views, likes and dislikes', function () {
+    let localVideosServer3 = []
+    let remoteVideosServer1 = []
+    let remoteVideosServer2 = []
+    let remoteVideosServer3 = []
+
+    before(async function () {
+      const res1 = await getVideosList(servers[0].url)
+      remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
+
+      const res2 = await getVideosList(servers[1].url)
+      remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
+
+      const res3 = await getVideosList(servers[2].url)
+      localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid)
+      remoteVideosServer3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
+    })
+
+    it('Should view multiple videos on owned servers', async function () {
+      this.timeout(10000)
+
+      const tasks: Promise<any>[] = []
+      tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
+      tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
+      tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
+      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
+
+      await Promise.all(tasks)
+
+      await wait(5000)
+
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+        const video0 = videos.find(v => v.uuid === localVideosServer3[0])
+        const video1 = videos.find(v => v.uuid === localVideosServer3[1])
+
+        expect(video0.views).to.equal(3)
+        expect(video1.views).to.equal(1)
+      }
+    })
+
+    it('Should view multiple videos on each servers', async function () {
+      this.timeout(15000)
+
+      const tasks: Promise<any>[] = []
+      tasks.push(viewVideo(servers[0].url, remoteVideosServer1[0]))
+      tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0]))
+      tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0]))
+      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[0]))
+      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
+      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
+      tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
+      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
+      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
+      tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
+
+      await Promise.all(tasks)
+
+      await wait(10000)
+
+      let baseVideos = null
+
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+
+        // Initialize base videos for future comparisons
+        if (baseVideos === null) {
+          baseVideos = videos
+          continue
+        }
+
+        for (const baseVideo of baseVideos) {
+          const sameVideo = videos.find(video => video.name === baseVideo.name)
+          expect(baseVideo.views).to.equal(sameVideo.views)
+        }
+      }
+    })
+
+    it('Should like and dislikes videos on different services', async function () {
+      this.timeout(20000)
+
+      const tasks: Promise<any>[] = []
+      tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
+      tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike'))
+      tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
+      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like'))
+      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike'))
+      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike'))
+      tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like'))
+
+      await Promise.all(tasks)
+
+      await wait(10000)
+
+      let baseVideos = null
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+
+        // Initialize base videos for future comparisons
+        if (baseVideos === null) {
+          baseVideos = videos
+          continue
+        }
+
+        for (const baseVideo of baseVideos) {
+          const sameVideo = videos.find(video => video.name === baseVideo.name)
+          expect(baseVideo.likes).to.equal(sameVideo.likes)
+          expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
+        }
+      }
+    })
+  })
+
+  describe('Should manipulate these videos', function () {
+    it('Should update the video 3 by asking server 3', async function () {
+      this.timeout(10000)
+
+      const attributes = {
+        name: 'my super video updated',
+        category: 10,
+        licence: 7,
+        language: 13,
+        nsfw: true,
+        description: 'my super description updated',
+        tags: [ 'tag_up_1', 'tag_up_2' ]
+      }
+
+      await updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, attributes)
+
+      await wait(5000)
+    })
+
+    it('Should have the video 3 updated on each server', async function () {
+      this.timeout(10000)
+
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+        const videoUpdated = videos.find(video => video.name === 'my super video updated')
+
+        expect(!!videoUpdated).to.be.true
+        expect(videoUpdated.category).to.equal(10)
+        expect(videoUpdated.categoryLabel).to.equal('Entertainment')
+        expect(videoUpdated.licence).to.equal(7)
+        expect(videoUpdated.licenceLabel).to.equal('Public Domain Dedication')
+        expect(videoUpdated.language).to.equal(13)
+        expect(videoUpdated.languageLabel).to.equal('French')
+        expect(videoUpdated.nsfw).to.be.ok
+        expect(videoUpdated.description).to.equal('my super description updated')
+        expect(dateIsValid(videoUpdated.updatedAt, 20000)).to.be.true
+
+        const res2 = await getVideo(server.url, videoUpdated.uuid)
+        const videoUpdatedDetails = res2.body
+        expect(videoUpdatedDetails.tags).to.deep.equal([ 'tag_up_1', 'tag_up_2' ])
+
+        const file = videoUpdatedDetails.files[0]
+        expect(file.magnetUri).to.have.lengthOf.above(2)
+        expect(file.resolution).to.equal(720)
+        expect(file.resolutionLabel).to.equal('720p')
+        expect(file.size).to.equal(292677)
+
+        const test = await testVideoImage(server.url, 'video_short3.webm', videoUpdated.thumbnailPath)
+        expect(test).to.equal(true)
+
+        // Avoid "duplicate torrent" errors
+        const refreshWebTorrent = true
+        const torrent = await webtorrentAdd(videoUpdatedDetails .files[0].magnetUri, refreshWebTorrent)
+        expect(torrent.files).to.be.an('array')
+        expect(torrent.files.length).to.equal(1)
+        expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+      }
+    })
+
+    it('Should remove the videos 3 and 3-2 by asking server 3', async function () {
+      this.timeout(10000)
+
+      await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id)
+      await removeVideo(servers[2].url, servers[2].accessToken, toRemove[1].id)
+
+      await wait(5000)
+    })
+
+    it('Should have videos 1 and 3 on each server', async function () {
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+
+        const videos = res.body.data
+        expect(videos).to.be.an('array')
+        expect(videos.length).to.equal(2)
+        expect(videos[0].name).not.to.equal(videos[1].name)
+        expect(videos[0].name).not.to.equal(toRemove[0].name)
+        expect(videos[1].name).not.to.equal(toRemove[0].name)
+        expect(videos[0].name).not.to.equal(toRemove[1].name)
+        expect(videos[1].name).not.to.equal(toRemove[1].name)
+
+        videoUUID = videos.find(video => video.name === 'my super name for server 1').uuid
+      }
+    })
+
+    it('Should get the same video by UUID on each server', async function () {
+      let baseVideo = null
+      for (const server of servers) {
+        const res = await getVideo(server.url, videoUUID)
+
+        const video = res.body
+
+        if (baseVideo === null) {
+          baseVideo = video
+          continue
+        }
+
+        expect(baseVideo.name).to.equal(video.name)
+        expect(baseVideo.uuid).to.equal(video.uuid)
+        expect(baseVideo.category).to.equal(video.category)
+        expect(baseVideo.language).to.equal(video.language)
+        expect(baseVideo.licence).to.equal(video.licence)
+        expect(baseVideo.category).to.equal(video.category)
+        expect(baseVideo.nsfw).to.equal(video.nsfw)
+        expect(baseVideo.accountName).to.equal(video.accountName)
+        expect(baseVideo.tags).to.deep.equal(video.tags)
+      }
+    })
+
+    it('Should get the preview from each server', async function () {
+      for (const server of servers) {
+        const res = await getVideo(server.url, videoUUID)
+        const video = res.body
+
+        const test = await testVideoImage(server.url, 'video_short1-preview.webm', video.previewPath)
+        expect(test).to.equal(true)
+      }
+    })
+  })
+
+  describe('Should comment these videos', function () {
+    it('Should add comment (threads and replies)', async function () {
+      this.timeout(25000)
+
+      {
+        const text = 'my super first comment'
+        await addVideoCommentThread(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, text)
+      }
+
+      {
+        const text = 'my super second comment'
+        await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, text)
+      }
+
+      await wait(5000)
+
+      {
+        const res = await getVideoCommentThreads(servers[1].url, videoUUID, 0, 5)
+        const threadId = res.body.data.find(c => c.text === 'my super first comment').id
+
+        const text = 'my super answer to thread 1'
+        await addVideoCommentReply(servers[ 1 ].url, servers[ 1 ].accessToken, videoUUID, threadId, text)
+      }
+
+      await wait(5000)
+
+      {
+        const res1 = await getVideoCommentThreads(servers[2].url, videoUUID, 0, 5)
+        const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
+
+        const res2 = await getVideoThreadComments(servers[2].url, videoUUID, threadId)
+        const childCommentId = res2.body.children[0].comment.id
+
+        const text3 = 'my second answer to thread 1'
+        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, threadId, text3)
+
+        const text2 = 'my super answer to answer of thread 1'
+        await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, childCommentId, text2)
+      }
+
+      await wait(5000)
+    })
+
+    it('Should have these threads', async function () {
+      for (const server of servers) {
+        const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
+
+        expect(res.body.total).to.equal(2)
+        expect(res.body.data).to.be.an('array')
+        expect(res.body.data).to.have.lengthOf(2)
+
+        {
+          const comment: VideoComment = res.body.data.find(c => c.text === 'my super first comment')
+          expect(comment).to.not.be.undefined
+          expect(comment.inReplyToCommentId).to.be.null
+          expect(comment.account.name).to.equal('root')
+          expect(comment.account.host).to.equal('localhost:9001')
+          expect(comment.totalReplies).to.equal(3)
+          expect(dateIsValid(comment.createdAt as string)).to.be.true
+          expect(dateIsValid(comment.updatedAt as string)).to.be.true
+        }
+
+        {
+          const comment: VideoComment = res.body.data.find(c => c.text === 'my super second comment')
+          expect(comment).to.not.be.undefined
+          expect(comment.inReplyToCommentId).to.be.null
+          expect(comment.account.name).to.equal('root')
+          expect(comment.account.host).to.equal('localhost:9003')
+          expect(comment.totalReplies).to.equal(0)
+          expect(dateIsValid(comment.createdAt as string)).to.be.true
+          expect(dateIsValid(comment.updatedAt as string)).to.be.true
+        }
+      }
+    })
+
+    it('Should have these comments', async function () {
+      for (const server of servers) {
+        const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
+        const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
+
+        const res2 = await getVideoThreadComments(server.url, videoUUID, threadId)
+
+        const tree: VideoCommentThreadTree = res2.body
+        expect(tree.comment.text).equal('my super first comment')
+        expect(tree.comment.account.name).equal('root')
+        expect(tree.comment.account.host).equal('localhost:9001')
+        expect(tree.children).to.have.lengthOf(2)
+
+        const firstChild = tree.children[0]
+        expect(firstChild.comment.text).to.equal('my super answer to thread 1')
+        expect(firstChild.comment.account.name).equal('root')
+        expect(firstChild.comment.account.host).equal('localhost:9002')
+        expect(firstChild.children).to.have.lengthOf(1)
+
+        const childOfFirstChild = firstChild.children[0]
+        expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
+        expect(childOfFirstChild.comment.account.name).equal('root')
+        expect(childOfFirstChild.comment.account.host).equal('localhost:9003')
+        expect(childOfFirstChild.children).to.have.lengthOf(0)
+
+        const secondChild = tree.children[1]
+        expect(secondChild.comment.text).to.equal('my second answer to thread 1')
+        expect(secondChild.comment.account.name).equal('root')
+        expect(secondChild.comment.account.host).equal('localhost:9003')
+        expect(secondChild.children).to.have.lengthOf(0)
+      }
+    })
+  })
+
+  describe('With minimum parameters', function () {
+    it('Should upload and propagate the video', async function () {
+      this.timeout(50000)
+
+      const path = '/api/v1/videos/upload'
+
+      const req = request(servers[1].url)
+        .post(path)
+        .set('Accept', 'application/json')
+        .set('Authorization', 'Bearer ' + servers[1].accessToken)
+        .field('name', 'minimum parameters')
+        .field('privacy', '1')
+        .field('nsfw', 'false')
+        .field('channelId', '1')
+
+      const filePath = join(__dirname, '..', 'api', 'fixtures', 'video_short.webm')
+
+      await req.attach('videofile', filePath)
+        .expect(200)
+
+      await wait(25000)
+
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+        const video = res.body.data.find(v => v.name === 'minimum parameters')
+
+        expect(video.name).to.equal('minimum parameters')
+        expect(video.category).to.equal(null)
+        expect(video.categoryLabel).to.equal('Misc')
+        expect(video.licence).to.equal(null)
+        expect(video.licenceLabel).to.equal('Unknown')
+        expect(video.language).to.equal(null)
+        expect(video.languageLabel).to.equal('Unknown')
+        expect(video.nsfw).to.not.be.ok
+        expect(video.description).to.equal(null)
+        expect(video.serverHost).to.equal('localhost:9002')
+        expect(video.accountName).to.equal('root')
+        expect(dateIsValid(video.createdAt)).to.be.true
+        expect(dateIsValid(video.updatedAt)).to.be.true
+      }
+    })
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/services.ts b/server/tests/api/videos/services.ts
new file mode 100644 (file)
index 0000000..1cda464
--- /dev/null
@@ -0,0 +1,85 @@
+/* tslint:disable:no-unused-expression */
+
+import 'mocha'
+import * as chai from 'chai'
+const expect = chai.expect
+
+import {
+  ServerInfo,
+  flushTests,
+  uploadVideo,
+  getVideosList,
+  setAccessTokensToServers,
+  killallServers,
+  getOEmbed
+} from '../../utils/index'
+import { runServer } from '../../utils/server/servers'
+
+describe('Test services', function () {
+  let server: ServerInfo = null
+
+  before(async function () {
+    this.timeout(10000)
+
+    await flushTests()
+
+    server = await runServer(1)
+
+    await setAccessTokensToServers([ server ])
+
+    const videoAttributes = {
+      name: 'my super name'
+    }
+    await uploadVideo(server.url, server.accessToken, videoAttributes)
+
+    const res = await getVideosList(server.url)
+    server.video = res.body.data[0]
+  })
+
+  it('Should have a valid oEmbed response', async function () {
+    const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid
+
+    const res = await getOEmbed(server.url, oembedUrl)
+    const expectedHtml = `<iframe width="560" height="315" src="http://localhost:9001/videos/embed/${server.video.uuid}" ` +
+                         'frameborder="0" allowfullscreen></iframe>'
+    const expectedThumbnailUrl = 'http://localhost:9001/static/previews/' + server.video.uuid + '.jpg'
+
+    expect(res.body.html).to.equal(expectedHtml)
+    expect(res.body.title).to.equal(server.video.name)
+    expect(res.body.author_name).to.equal(server.video.accountName)
+    expect(res.body.width).to.equal(560)
+    expect(res.body.height).to.equal(315)
+    expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl)
+    expect(res.body.thumbnail_width).to.equal(560)
+    expect(res.body.thumbnail_height).to.equal(315)
+  })
+
+  it('Should have a valid oEmbed response with small max height query', async function () {
+    const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid
+    const format = 'json'
+    const maxHeight = 50
+    const maxWidth = 50
+
+    const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth)
+    const expectedHtml = `<iframe width="50" height="50" src="http://localhost:9001/videos/embed/${server.video.uuid}" ` +
+                         'frameborder="0" allowfullscreen></iframe>'
+
+    expect(res.body.html).to.equal(expectedHtml)
+    expect(res.body.title).to.equal(server.video.name)
+    expect(res.body.author_name).to.equal(server.video.accountName)
+    expect(res.body.height).to.equal(50)
+    expect(res.body.width).to.equal(50)
+    expect(res.body).to.not.have.property('thumbnail_url')
+    expect(res.body).to.not.have.property('thumbnail_width')
+    expect(res.body).to.not.have.property('thumbnail_height')
+  })
+
+  after(async function () {
+    killallServers([ server ])
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
new file mode 100644 (file)
index 0000000..0272365
--- /dev/null
@@ -0,0 +1,701 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import { keyBy } from 'lodash'
+import 'mocha'
+import { join } from 'path'
+import {
+  dateIsValid,
+  flushTests,
+  getVideo,
+  getVideoCategories,
+  getVideoLanguages,
+  getVideoLicences,
+  getVideoPrivacies,
+  getVideosList,
+  getVideosListPagination,
+  getVideosListSort,
+  killallServers,
+  rateVideo,
+  readdirPromise,
+  removeVideo,
+  runServer,
+  searchVideo,
+  searchVideoWithPagination,
+  searchVideoWithSort,
+  ServerInfo,
+  setAccessTokensToServers,
+  testVideoImage,
+  updateVideo,
+  uploadVideo,
+  wait,
+  webtorrentAdd
+} from '../../utils/index'
+import { viewVideo } from '../../utils/videos/videos'
+
+const expect = chai.expect
+
+describe('Test a single server', function () {
+  let server: ServerInfo = null
+  let videoId = -1
+  let videoUUID = ''
+  let videosListBase: any[] = null
+
+  before(async function () {
+    this.timeout(10000)
+
+    await flushTests()
+
+    server = await runServer(1)
+
+    await setAccessTokensToServers([ server ])
+  })
+
+  it('Should list video categories', async function () {
+    const res = await getVideoCategories(server.url)
+
+    const categories = res.body
+    expect(Object.keys(categories)).to.have.length.above(10)
+
+    expect(categories[11]).to.equal('News')
+  })
+
+  it('Should list video licences', async function () {
+    const res = await getVideoLicences(server.url)
+
+    const licences = res.body
+    expect(Object.keys(licences)).to.have.length.above(5)
+
+    expect(licences[3]).to.equal('Attribution - No Derivatives')
+  })
+
+  it('Should list video languages', async function () {
+    const res = await getVideoLanguages(server.url)
+
+    const languages = res.body
+    expect(Object.keys(languages)).to.have.length.above(5)
+
+    expect(languages[3]).to.equal('Mandarin')
+  })
+
+  it('Should list video privacies', async function () {
+    const res = await getVideoPrivacies(server.url)
+
+    const privacies = res.body
+    expect(Object.keys(privacies)).to.have.length.at.least(3)
+
+    expect(privacies[3]).to.equal('Private')
+  })
+
+  it('Should not have videos', async function () {
+    const res = await getVideosList(server.url)
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(0)
+  })
+
+  it('Should upload the video', async function () {
+    const videoAttributes = {
+      name: 'my super name',
+      category: 2,
+      nsfw: true,
+      licence: 6,
+      tags: [ 'tag1', 'tag2', 'tag3' ]
+    }
+    const res = await uploadVideo(server.url, server.accessToken, videoAttributes)
+    expect(res.body.video).to.not.be.undefined
+    expect(res.body.video.id).to.equal(1)
+    expect(res.body.video.uuid).to.have.length.above(5)
+  })
+
+  it('Should seed the uploaded video', async function () {
+    // Yes, this could be long
+    this.timeout(60000)
+
+    const res = await getVideosList(server.url)
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(1)
+
+    const video = res.body.data[0]
+    expect(video.name).to.equal('my super name')
+    expect(video.category).to.equal(2)
+    expect(video.categoryLabel).to.equal('Films')
+    expect(video.licence).to.equal(6)
+    expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
+    expect(video.language).to.equal(3)
+    expect(video.languageLabel).to.equal('Mandarin')
+    expect(video.nsfw).to.be.ok
+    expect(video.description).to.equal('my super description')
+    expect(video.serverHost).to.equal('localhost:9001')
+    expect(video.accountName).to.equal('root')
+    expect(video.isLocal).to.be.true
+    expect(dateIsValid(video.createdAt)).to.be.true
+    expect(dateIsValid(video.updatedAt)).to.be.true
+
+    const res2 = await getVideo(server.url, res.body.data[0].id)
+    const videoDetails = res2.body
+
+    expect(videoDetails.files).to.have.lengthOf(1)
+
+    const file = videoDetails.files[0]
+    const magnetUri = file.magnetUri
+    expect(file.magnetUri).to.have.lengthOf.above(2)
+    expect(file.torrentUrl).to.equal(`${server.url}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
+    expect(file.fileUrl).to.equal(`${server.url}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`)
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
+    expect(file.size).to.equal(218910)
+
+    const test = await testVideoImage(server.url, 'video_short.webm', videoDetails.thumbnailPath)
+    expect(test).to.equal(true)
+
+    videoId = videoDetails.id
+    videoUUID = videoDetails.uuid
+
+    const torrent = await webtorrentAdd(magnetUri)
+    expect(torrent.files).to.be.an('array')
+    expect(torrent.files.length).to.equal(1)
+    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+  })
+
+  it('Should get the video', async function () {
+    // Yes, this could be long
+    this.timeout(60000)
+
+    const res = await getVideo(server.url, videoId)
+
+    const video = res.body
+    expect(video.name).to.equal('my super name')
+    expect(video.category).to.equal(2)
+    expect(video.categoryLabel).to.equal('Films')
+    expect(video.licence).to.equal(6)
+    expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
+    expect(video.language).to.equal(3)
+    expect(video.languageLabel).to.equal('Mandarin')
+    expect(video.nsfw).to.be.ok
+    expect(video.description).to.equal('my super description')
+    expect(video.serverHost).to.equal('localhost:9001')
+    expect(video.accountName).to.equal('root')
+    expect(video.isLocal).to.be.true
+    expect(dateIsValid(video.createdAt)).to.be.true
+    expect(dateIsValid(video.updatedAt)).to.be.true
+    expect(video.channel.name).to.equal('Default root channel')
+    expect(video.channel.isLocal).to.be.true
+    expect(dateIsValid(video.channel.createdAt)).to.be.true
+    expect(dateIsValid(video.channel.updatedAt)).to.be.true
+
+    expect(video.files).to.have.lengthOf(1)
+
+    const file = video.files[0]
+    expect(file.magnetUri).to.have.lengthOf.above(2)
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
+    expect(file.size).to.equal(218910)
+
+    const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
+    expect(test).to.equal(true)
+
+    // Wait the async views increment
+    await wait(500)
+  })
+
+  it('Should get the video by UUID', async function () {
+    // Yes, this could be long
+    this.timeout(60000)
+
+    const res = await getVideo(server.url, videoUUID)
+
+    const video = res.body
+    expect(video.name).to.equal('my super name')
+
+    // Wait the async views increment
+    await wait(500)
+  })
+
+  it('Should have the views updated', async function () {
+    await viewVideo(server.url, videoId)
+    await viewVideo(server.url, videoId)
+    await viewVideo(server.url, videoId)
+
+    const res = await getVideo(server.url, videoId)
+
+    const video = res.body
+    expect(video.views).to.equal(3)
+  })
+
+  it('Should search the video by name', async function () {
+    const res = await searchVideo(server.url, 'my')
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(1)
+
+    const video = res.body.data[0]
+    expect(video.name).to.equal('my super name')
+    expect(video.category).to.equal(2)
+    expect(video.categoryLabel).to.equal('Films')
+    expect(video.licence).to.equal(6)
+    expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
+    expect(video.language).to.equal(3)
+    expect(video.languageLabel).to.equal('Mandarin')
+    expect(video.nsfw).to.be.ok
+    expect(video.description).to.equal('my super description')
+    expect(video.serverHost).to.equal('localhost:9001')
+    expect(video.accountName).to.equal('root')
+    expect(video.isLocal).to.be.true
+    expect(dateIsValid(video.createdAt)).to.be.true
+    expect(dateIsValid(video.updatedAt)).to.be.true
+
+    const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
+    expect(test).to.equal(true)
+  })
+
+  // Not implemented yet
+  // it('Should search the video by serverHost', async function () {
+  //     const res = await   videosUtils.searchVideo(server.url, '9001', 'host')
+
+  //     expect(res.body.total).to.equal(1)
+  //     expect(res.body.data).to.be.an('array')
+  //     expect(res.body.data.length).to.equal(1)
+
+  //     const video = res.body.data[0]
+  //     expect(video.name).to.equal('my super name')
+  //     expect(video.description).to.equal('my super description')
+  //     expect(video.serverHost).to.equal('localhost:9001')
+  //     expect(video.author).to.equal('root')
+  //     expect(video.isLocal).to.be.true
+  //     expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
+  //     expect(dateIsValid(video.createdAt)).to.be.true
+  //     expect(dateIsValid(video.updatedAt)).to.be.true
+
+  //     const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
+  //       expect(test).to.equal(true)
+
+  //       done()
+  //     })
+  //   })
+  // })
+
+  // Not implemented yet
+  // it('Should search the video by tag', async function () {
+  //   const res = await searchVideo(server.url, 'tag1')
+  //
+  //   expect(res.body.total).to.equal(1)
+  //   expect(res.body.data).to.be.an('array')
+  //   expect(res.body.data.length).to.equal(1)
+  //
+  //   const video = res.body.data[0]
+  //   expect(video.name).to.equal('my super name')
+  //   expect(video.category).to.equal(2)
+  //   expect(video.categoryLabel).to.equal('Films')
+  //   expect(video.licence).to.equal(6)
+  //   expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives')
+  //   expect(video.language).to.equal(3)
+  //   expect(video.languageLabel).to.equal('Mandarin')
+  //   expect(video.nsfw).to.be.ok
+  //   expect(video.description).to.equal('my super description')
+  //   expect(video.serverHost).to.equal('localhost:9001')
+  //   expect(video.accountName).to.equal('root')
+  //   expect(video.isLocal).to.be.true
+  //   expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
+  //   expect(dateIsValid(video.createdAt)).to.be.true
+  //   expect(dateIsValid(video.updatedAt)).to.be.true
+  //
+  //   const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
+  //   expect(test).to.equal(true)
+  // })
+
+  it('Should not find a search by name', async function () {
+    const res = await searchVideo(server.url, 'hello')
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(0)
+  })
+
+  // Not implemented yet
+  // it('Should not find a search by author', async function () {
+  //   const res = await searchVideo(server.url, 'hello')
+  //
+  //   expect(res.body.total).to.equal(0)
+  //   expect(res.body.data).to.be.an('array')
+  //   expect(res.body.data.length).to.equal(0)
+  // })
+  //
+  // Not implemented yet
+  // it('Should not find a search by tag', async function () {
+  //   const res = await searchVideo(server.url, 'hello')
+  //
+  //   expect(res.body.total).to.equal(0)
+  //   expect(res.body.data).to.be.an('array')
+  //   expect(res.body.data.length).to.equal(0)
+  // })
+
+  it('Should remove the video', async function () {
+    await removeVideo(server.url, server.accessToken, videoId)
+
+    const files1 = await readdirPromise(join(__dirname, '..', '..', '..', 'test1/videos/'))
+    expect(files1).to.have.lengthOf(0)
+
+    const files2 = await readdirPromise(join(__dirname, '..', '..', '..', 'test1/thumbnails/'))
+    expect(files2).to.have.lengthOf(0)
+  })
+
+  it('Should not have videos', async function () {
+    const res = await getVideosList(server.url)
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(0)
+  })
+
+  it('Should upload 6 videos', async function () {
+    this.timeout(25000)
+
+    const videos = [
+      'video_short.mp4', 'video_short.ogv', 'video_short.webm',
+      'video_short1.webm', 'video_short2.webm', 'video_short3.webm'
+    ]
+
+    const tasks: Promise<any>[] = []
+    for (const video of videos) {
+      const videoAttributes = {
+        name: video + ' name',
+        description: video + ' description',
+        category: 2,
+        licence: 1,
+        language: 1,
+        nsfw: true,
+        tags: [ 'tag1', 'tag2', 'tag3' ],
+        fixture: video
+      }
+
+      const p = uploadVideo(server.url, server.accessToken, videoAttributes)
+      tasks.push(p)
+    }
+
+    await Promise.all(tasks)
+  })
+
+  it('Should have the correct durations', async function () {
+    const res = await getVideosList(server.url)
+
+    expect(res.body.total).to.equal(6)
+    const videos = res.body.data
+    expect(videos).to.be.an('array')
+    expect(videos).to.have.lengthOf(6)
+
+    const videosByName = keyBy<{ duration: number }>(videos, 'name')
+    expect(videosByName['video_short.mp4 name'].duration).to.equal(5)
+    expect(videosByName['video_short.ogv name'].duration).to.equal(5)
+    expect(videosByName['video_short.webm name'].duration).to.equal(5)
+    expect(videosByName['video_short1.webm name'].duration).to.equal(10)
+    expect(videosByName['video_short2.webm name'].duration).to.equal(5)
+    expect(videosByName['video_short3.webm name'].duration).to.equal(5)
+  })
+
+  it('Should have the correct thumbnails', async function () {
+    const res = await getVideosList(server.url)
+
+    const videos = res.body.data
+    // For the next test
+    videosListBase = videos
+
+    for (const video of videos) {
+      const videoName = video.name.replace(' name', '')
+      const test = await testVideoImage(server.url, videoName, video.thumbnailPath)
+
+      expect(test).to.equal(true)
+    }
+  })
+
+  it('Should list only the two first videos', async function () {
+    const res = await getVideosListPagination(server.url, 0, 2, 'name')
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(6)
+    expect(videos.length).to.equal(2)
+    expect(videos[0].name).to.equal(videosListBase[0].name)
+    expect(videos[1].name).to.equal(videosListBase[1].name)
+  })
+
+  it('Should list only the next three videos', async function () {
+    const res = await getVideosListPagination(server.url, 2, 3, 'name')
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(6)
+    expect(videos.length).to.equal(3)
+    expect(videos[0].name).to.equal(videosListBase[2].name)
+    expect(videos[1].name).to.equal(videosListBase[3].name)
+    expect(videos[2].name).to.equal(videosListBase[4].name)
+  })
+
+  it('Should list the last video', async function () {
+    const res = await getVideosListPagination(server.url, 5, 6, 'name')
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(6)
+    expect(videos.length).to.equal(1)
+    expect(videos[0].name).to.equal(videosListBase[5].name)
+  })
+
+  it('Should search the first video', async function () {
+    const res = await searchVideoWithPagination(server.url, 'webm', 0, 1, 'name')
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(4)
+    expect(videos.length).to.equal(1)
+    expect(videos[0].name).to.equal('video_short1.webm name')
+  })
+
+  it('Should search the last two videos', async function () {
+    const res = await searchVideoWithPagination(server.url, 'webm', 2, 2, 'name')
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(4)
+    expect(videos.length).to.equal(2)
+    expect(videos[0].name).to.equal('video_short3.webm name')
+    expect(videos[1].name).to.equal('video_short.webm name')
+  })
+
+  it('Should search all the webm videos', async function () {
+    const res = await searchVideoWithPagination(server.url, 'webm', 0, 15)
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(4)
+    expect(videos.length).to.equal(4)
+  })
+
+  // Not implemented yet
+  // it('Should search all the root author videos', async function () {
+  //   const res = await searchVideoWithPagination(server.url, 'root', 0, 15)
+  //
+  //   const videos = res.body.data
+  //   expect(res.body.total).to.equal(6)
+  //   expect(videos.length).to.equal(6)
+  // })
+
+  // Not implemented yet
+  // it('Should search all the 9001 port videos', async function () {
+  // const res = await   videosUtils.searchVideoWithPagination(server.url, '9001', 'host', 0, 15)
+
+  //     const videos = res.body.data
+  //     expect(res.body.total).to.equal(6)
+  //     expect(videos.length).to.equal(6)
+
+  //     done()
+  //   })
+  // })
+
+  // it('Should search all the localhost videos', async function () {
+  // const res = await   videosUtils.searchVideoWithPagination(server.url, 'localhost', 'host', 0, 15)
+
+  //     const videos = res.body.data
+  //     expect(res.body.total).to.equal(6)
+  //     expect(videos.length).to.equal(6)
+
+  //     done()
+  //   })
+  // })
+
+  it('Should list and sort by name in descending order', async function () {
+    const res = await getVideosListSort(server.url, '-name')
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(6)
+    expect(videos.length).to.equal(6)
+    expect(videos[0].name).to.equal('video_short.webm name')
+    expect(videos[1].name).to.equal('video_short.ogv name')
+    expect(videos[2].name).to.equal('video_short.mp4 name')
+    expect(videos[3].name).to.equal('video_short3.webm name')
+    expect(videos[4].name).to.equal('video_short2.webm name')
+    expect(videos[5].name).to.equal('video_short1.webm name')
+  })
+
+  it('Should search and sort by name in ascending order', async function () {
+    const res = await searchVideoWithSort(server.url, 'webm', 'name')
+
+    const videos = res.body.data
+    expect(res.body.total).to.equal(4)
+    expect(videos.length).to.equal(4)
+
+    expect(videos[0].name).to.equal('video_short1.webm name')
+    expect(videos[1].name).to.equal('video_short2.webm name')
+    expect(videos[2].name).to.equal('video_short3.webm name')
+    expect(videos[3].name).to.equal('video_short.webm name')
+
+    videoId = videos[2].id
+  })
+
+  it('Should update a video', async function () {
+    const attributes = {
+      name: 'my super video updated',
+      category: 4,
+      licence: 2,
+      language: 5,
+      nsfw: false,
+      description: 'my super description updated',
+      tags: [ 'tagup1', 'tagup2' ]
+    }
+    await updateVideo(server.url, server.accessToken, videoId, attributes)
+  })
+
+  it('Should have the video updated', async function () {
+    this.timeout(60000)
+
+    const res = await getVideo(server.url, videoId)
+
+    const video = res.body
+
+    expect(video.name).to.equal('my super video updated')
+    expect(video.category).to.equal(4)
+    expect(video.categoryLabel).to.equal('Art')
+    expect(video.licence).to.equal(2)
+    expect(video.licenceLabel).to.equal('Attribution - Share Alike')
+    expect(video.language).to.equal(5)
+    expect(video.languageLabel).to.equal('Arabic')
+    expect(video.nsfw).to.be.ok
+    expect(video.description).to.equal('my super description updated')
+    expect(video.serverHost).to.equal('localhost:9001')
+    expect(video.accountName).to.equal('root')
+    expect(video.account.name).to.equal('root')
+    expect(video.isLocal).to.be.true
+    expect(video.tags).to.deep.equal([ 'tagup1', 'tagup2' ])
+    expect(dateIsValid(video.createdAt)).to.be.true
+    expect(dateIsValid(video.updatedAt)).to.be.true
+
+    expect(video.channel.name).to.equal('Default root channel')
+    expect(video.channel.isLocal).to.be.true
+    expect(dateIsValid(video.channel.createdAt)).to.be.true
+    expect(dateIsValid(video.channel.updatedAt)).to.be.true
+
+    expect(video.files).to.have.lengthOf(1)
+
+    const file = video.files[0]
+    const magnetUri = file.magnetUri
+    expect(file.magnetUri).to.have.lengthOf.above(2)
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
+    expect(file.size).to.equal(292677)
+
+    const test = await testVideoImage(server.url, 'video_short3.webm', video.thumbnailPath)
+    expect(test).to.equal(true)
+
+    const torrent = await webtorrentAdd(magnetUri)
+    expect(torrent.files).to.be.an('array')
+    expect(torrent.files.length).to.equal(1)
+    expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+  })
+
+  it('Should update only the tags of a video', async function () {
+    const attributes = {
+      tags: [ 'tag1', 'tag2', 'supertag' ]
+    }
+
+    await updateVideo(server.url, server.accessToken, videoId, attributes)
+
+    const res = await getVideo(server.url, videoId)
+    const video = res.body
+
+    expect(video.name).to.equal('my super video updated')
+    expect(video.category).to.equal(4)
+    expect(video.categoryLabel).to.equal('Art')
+    expect(video.licence).to.equal(2)
+    expect(video.licenceLabel).to.equal('Attribution - Share Alike')
+    expect(video.language).to.equal(5)
+    expect(video.languageLabel).to.equal('Arabic')
+    expect(video.nsfw).to.be.ok
+    expect(video.description).to.equal('my super description updated')
+    expect(video.serverHost).to.equal('localhost:9001')
+    expect(video.accountName).to.equal('root')
+    expect(video.isLocal).to.be.true
+    expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ])
+    expect(dateIsValid(video.createdAt)).to.be.true
+    expect(dateIsValid(video.updatedAt)).to.be.true
+
+    expect(video.channel.name).to.equal('Default root channel')
+    expect(video.channel.isLocal).to.be.true
+    expect(dateIsValid(video.channel.createdAt)).to.be.true
+    expect(dateIsValid(video.channel.updatedAt)).to.be.true
+
+    expect(video.files).to.have.lengthOf(1)
+
+    const file = video.files[0]
+    expect(file.magnetUri).to.have.lengthOf.above(2)
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
+    expect(file.size).to.equal(292677)
+  })
+
+  it('Should update only the description of a video', async function () {
+    const attributes = {
+      description: 'hello everybody'
+    }
+
+    await updateVideo(server.url, server.accessToken, videoId, attributes)
+
+    const res = await getVideo(server.url, videoId)
+    const video = res.body
+
+    expect(video.name).to.equal('my super video updated')
+    expect(video.category).to.equal(4)
+    expect(video.categoryLabel).to.equal('Art')
+    expect(video.licence).to.equal(2)
+    expect(video.licenceLabel).to.equal('Attribution - Share Alike')
+    expect(video.language).to.equal(5)
+    expect(video.languageLabel).to.equal('Arabic')
+    expect(video.nsfw).to.be.ok
+    expect(video.description).to.equal('hello everybody')
+    expect(video.serverHost).to.equal('localhost:9001')
+    expect(video.accountName).to.equal('root')
+    expect(video.isLocal).to.be.true
+    expect(video.tags).to.deep.equal([ 'supertag', 'tag1', 'tag2' ])
+    expect(dateIsValid(video.createdAt)).to.be.true
+    expect(dateIsValid(video.updatedAt)).to.be.true
+
+    expect(video.channel.name).to.equal('Default root channel')
+    expect(video.channel.isLocal).to.be.true
+    expect(dateIsValid(video.channel.createdAt)).to.be.true
+    expect(dateIsValid(video.channel.updatedAt)).to.be.true
+
+    expect(video.files).to.have.lengthOf(1)
+
+    const file = video.files[0]
+    expect(file.magnetUri).to.have.lengthOf.above(2)
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
+    expect(file.size).to.equal(292677)
+  })
+
+  it('Should like a video', async function () {
+    await rateVideo(server.url, server.accessToken, videoId, 'like')
+
+    const res = await getVideo(server.url, videoId)
+    const video = res.body
+
+    expect(video.likes).to.equal(1)
+    expect(video.dislikes).to.equal(0)
+  })
+
+  it('Should dislike the same video', async function () {
+    await rateVideo(server.url, server.accessToken, videoId, 'dislike')
+
+    const res = await getVideo(server.url, videoId)
+    const video = res.body
+
+    expect(video.likes).to.equal(0)
+    expect(video.dislikes).to.equal(1)
+  })
+
+  after(async function () {
+    killallServers([ server ])
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-abuse.ts b/server/tests/api/videos/video-abuse.ts
new file mode 100644 (file)
index 0000000..3fcf5d8
--- /dev/null
@@ -0,0 +1,145 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import {
+  flushAndRunMultipleServers,
+  flushTests,
+  getVideoAbusesList,
+  getVideosList,
+  killallServers,
+  reportVideoAbuse,
+  ServerInfo,
+  setAccessTokensToServers,
+  uploadVideo,
+  wait
+} from '../../utils/index'
+import { doubleFollow } from '../../utils/server/follows'
+
+const expect = chai.expect
+
+describe('Test video abuses', function () {
+  let servers: ServerInfo[] = []
+
+  before(async function () {
+    this.timeout(50000)
+
+    // Run servers
+    servers = await flushAndRunMultipleServers(2)
+
+    // Get the access tokens
+    await setAccessTokensToServers(servers)
+
+    // Server 1 and server 2 follow each other
+    await doubleFollow(servers[0], servers[1])
+
+    // Upload some videos on each servers
+    const video1Attributes = {
+      name: 'my super name for server 1',
+      description: 'my super description for server 1'
+    }
+    await uploadVideo(servers[0].url, servers[0].accessToken, video1Attributes)
+
+    const video2Attributes = {
+      name: 'my super name for server 2',
+      description: 'my super description for server 2'
+    }
+    await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes)
+
+    // Wait videos propagation, server 2 has transcoding enabled
+    await wait(15000)
+
+    const res = await getVideosList(servers[0].url)
+    const videos = res.body.data
+
+    expect(videos.length).to.equal(2)
+
+    servers[0].video = videos.find(video => video.name === 'my super name for server 1')
+    servers[1].video = videos.find(video => video.name === 'my super name for server 2')
+  })
+
+  it('Should not have video abuses', async function () {
+    const res = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(0)
+  })
+
+  it('Should report abuse on a local video', async function () {
+    this.timeout(10000)
+
+    const reason = 'my super bad reason'
+    await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[0].video.id, reason)
+
+    // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2
+    await wait(5000)
+  })
+
+  it('Should have 1 video abuses on server 1 and 0 on server 2', async function () {
+    const res1 = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
+
+    expect(res1.body.total).to.equal(1)
+    expect(res1.body.data).to.be.an('array')
+    expect(res1.body.data.length).to.equal(1)
+
+    const abuse = res1.body.data[0]
+    expect(abuse.reason).to.equal('my super bad reason')
+    expect(abuse.reporterUsername).to.equal('root')
+    expect(abuse.reporterServerHost).to.equal('localhost:9001')
+    expect(abuse.videoId).to.equal(servers[0].video.id)
+
+    const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
+    expect(res2.body.total).to.equal(0)
+    expect(res2.body.data).to.be.an('array')
+    expect(res2.body.data.length).to.equal(0)
+  })
+
+  it('Should report abuse on a remote video', async function () {
+    this.timeout(10000)
+
+    const reason = 'my super bad reason 2'
+    await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[1].video.id, reason)
+
+    // We wait requests propagation
+    await wait(5000)
+  })
+
+  it('Should have 2 video abuse on server 1 and 1 on server 2', async function () {
+    const res1 = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
+    expect(res1.body.total).to.equal(2)
+    expect(res1.body.data).to.be.an('array')
+    expect(res1.body.data.length).to.equal(2)
+
+    const abuse1 = res1.body.data[0]
+    expect(abuse1.reason).to.equal('my super bad reason')
+    expect(abuse1.reporterUsername).to.equal('root')
+    expect(abuse1.reporterServerHost).to.equal('localhost:9001')
+    expect(abuse1.videoId).to.equal(servers[0].video.id)
+
+    const abuse2 = res1.body.data[1]
+    expect(abuse2.reason).to.equal('my super bad reason 2')
+    expect(abuse2.reporterUsername).to.equal('root')
+    expect(abuse2.reporterServerHost).to.equal('localhost:9001')
+    expect(abuse2.videoId).to.equal(servers[1].video.id)
+
+    const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
+    expect(res2.body.total).to.equal(1)
+    expect(res2.body.data).to.be.an('array')
+    expect(res2.body.data.length).to.equal(1)
+
+    const abuse3 = res2.body.data[0]
+    expect(abuse3.reason).to.equal('my super bad reason 2')
+    expect(abuse3.reporterUsername).to.equal('root')
+    expect(abuse3.reporterServerHost).to.equal('localhost:9001')
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-blacklist-management.ts b/server/tests/api/videos/video-blacklist-management.ts
new file mode 100644 (file)
index 0000000..db79784
--- /dev/null
@@ -0,0 +1,162 @@
+/* tslint:disable:no-unused-expressions */
+
+import * as chai from 'chai'
+import * as lodash from 'lodash'
+import 'mocha'
+import {
+  addVideoToBlacklist,
+  flushAndRunMultipleServers,
+  flushTests,
+  getBlacklistedVideosList,
+  getSortedBlacklistedVideosList,
+  getVideosList,
+  killallServers,
+  removeVideoFromBlacklist,
+  ServerInfo,
+  setAccessTokensToServers,
+  uploadVideo,
+  wait
+} from '../../utils/index'
+import { doubleFollow } from '../../utils/server/follows'
+
+const expect = chai.expect
+const orderBy = lodash.orderBy
+
+describe('Test video blacklist management', function () {
+  let servers: ServerInfo[] = []
+
+  async function blacklistVideosOnServer (server: ServerInfo) {
+    const res = await getVideosList(server.url)
+
+    const videos = res.body.data
+    for (let video of videos) {
+      await addVideoToBlacklist(server.url, server.accessToken, video.id)
+    }
+  }
+
+  before(async function () {
+    this.timeout(50000)
+
+    // Run servers
+    servers = await flushAndRunMultipleServers(2)
+
+    // Get the access tokens
+    await setAccessTokensToServers(servers)
+
+    // Server 1 and server 2 follow each other
+    await doubleFollow(servers[0], servers[1])
+
+    // Upload 2 videos on server 2
+    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 1st video', description: 'A video on server 2' })
+    await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'My 2nd video', description: 'A video on server 2' })
+
+    // Wait videos propagation, server 2 has transcoding enabled
+    await wait(15000)
+
+    // Blacklist the two videos on server 1
+    await blacklistVideosOnServer(servers[0])
+  })
+
+  describe('When listing blacklisted videos', function () {
+    it('Should display all the blacklisted videos', async function () {
+      const res = await getBlacklistedVideosList(servers[0].url, servers[0].accessToken)
+
+      expect(res.body.total).to.equal(2)
+
+      const videos = res.body.data
+      expect(videos).to.be.an('array')
+      expect(videos.length).to.equal(2)
+    })
+
+    it('Should get the correct sort when sorting by descending id', async function () {
+      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-id')
+      expect(res.body.total).to.equal(2)
+
+      const videos = res.body.data
+      expect(videos).to.be.an('array')
+      expect(videos.length).to.equal(2)
+
+      const result = orderBy(res.body.data, [ 'id' ], [ 'desc' ])
+
+      expect(videos).to.deep.equal(result)
+    })
+
+    it('Should get the correct sort when sorting by descending video name', async function () {
+      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
+      expect(res.body.total).to.equal(2)
+
+      const videos = res.body.data
+      expect(videos).to.be.an('array')
+      expect(videos.length).to.equal(2)
+
+      const result = orderBy(res.body.data, [ 'name' ], [ 'desc' ])
+
+      expect(videos).to.deep.equal(result)
+    })
+
+    it('Should get the correct sort when sorting by ascending creation date', async function () {
+      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, 'createdAt')
+      expect(res.body.total).to.equal(2)
+
+      const videos = res.body.data
+      expect(videos).to.be.an('array')
+      expect(videos.length).to.equal(2)
+
+      const result = orderBy(res.body.data, [ 'createdAt' ])
+
+      expect(videos).to.deep.equal(result)
+    })
+  })
+
+  describe('When removing a blacklisted video', function () {
+    let videoToRemove
+    let blacklist = []
+
+    it('Should not have any video in videos list on server 1', async function () {
+      const res = await getVideosList(servers[0].url)
+      expect(res.body.total).to.equal(0)
+      expect(res.body.data).to.be.an('array')
+      expect(res.body.data.length).to.equal(0)
+    })
+
+    it('Should remove a video from the blacklist on server 1', async function () {
+      // Get one video in the blacklist
+      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
+      videoToRemove = res.body.data[0]
+      blacklist = res.body.data.slice(1)
+
+      // Remove it
+      await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, videoToRemove.videoId)
+    })
+
+    it('Should have the ex-blacklisted video in videos list on server 1', async function () {
+      const res = await getVideosList(servers[0].url)
+      expect(res.body.total).to.equal(1)
+
+      const videos = res.body.data
+      expect(videos).to.be.an('array')
+      expect(videos.length).to.equal(1)
+
+      expect(videos[0].name).to.equal(videoToRemove.name)
+      expect(videos[0].id).to.equal(videoToRemove.videoId)
+    })
+
+    it('Should not have the ex-blacklisted video in videos blacklist list on server 1', async function () {
+      const res = await getSortedBlacklistedVideosList(servers[0].url, servers[0].accessToken, '-name')
+      expect(res.body.total).to.equal(1)
+
+      const videos = res.body.data
+      expect(videos).to.be.an('array')
+      expect(videos.length).to.equal(1)
+      expect(videos).to.deep.equal(blacklist)
+    })
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-blacklist.ts b/server/tests/api/videos/video-blacklist.ts
new file mode 100644 (file)
index 0000000..d1cefa5
--- /dev/null
@@ -0,0 +1,98 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import {
+  addVideoToBlacklist,
+  flushAndRunMultipleServers,
+  flushTests,
+  getVideosList,
+  killallServers,
+  searchVideo,
+  ServerInfo,
+  setAccessTokensToServers,
+  uploadVideo,
+  wait
+} from '../../utils/index'
+import { doubleFollow } from '../../utils/server/follows'
+
+const expect = chai.expect
+
+describe('Test video blacklists', function () {
+  let servers: ServerInfo[] = []
+
+  before(async function () {
+    this.timeout(50000)
+
+    // Run servers
+    servers = await flushAndRunMultipleServers(2)
+
+    // Get the access tokens
+    await setAccessTokensToServers(servers)
+
+    // Server 1 and server 2 follow each other
+    await doubleFollow(servers[0], servers[1])
+
+    // Upload a video on server 2
+    const videoAttributes = {
+      name: 'my super name for server 2',
+      description: 'my super description for server 2'
+    }
+    await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes)
+
+    // Wait videos propagation, server 2 has transcoding enabled
+    await wait(10000)
+
+    const res = await getVideosList(servers[0].url)
+    const videos = res.body.data
+
+    expect(videos.length).to.equal(1)
+
+    servers[0].remoteVideo = videos.find(video => video.name === 'my super name for server 2')
+  })
+
+  it('Should blacklist a remote video on server 1', async function () {
+    await addVideoToBlacklist(servers[0].url, servers[0].accessToken, servers[0].remoteVideo.id)
+  })
+
+  it('Should not have the video blacklisted in videos list on server 1', async function () {
+    const res = await getVideosList(servers[0].url)
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(0)
+  })
+
+  it('Should not have the video blacklisted in videos search on server 1', async function () {
+    const res = await searchVideo(servers[0].url, 'name')
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(0)
+  })
+
+  it('Should have the blacklisted video in videos list on server 2', async function () {
+    const res = await getVideosList(servers[1].url)
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(1)
+  })
+
+  it('Should have the video blacklisted in videos search on server 2', async function () {
+    const res = await searchVideo(servers[1].url, 'name')
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data.length).to.equal(1)
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
new file mode 100644 (file)
index 0000000..454a96d
--- /dev/null
@@ -0,0 +1,139 @@
+/* tslint:disable:no-unused-expression */
+
+import 'mocha'
+import * as chai from 'chai'
+const expect = chai.expect
+
+import {
+  ServerInfo,
+  flushTests,
+  runServer,
+  setAccessTokensToServers,
+  killallServers,
+  getMyUserInformation,
+  getVideoChannelsList,
+  addVideoChannel,
+  getAccountVideoChannelsList,
+  updateVideoChannel,
+  deleteVideoChannel,
+  getVideoChannel
+} from '../../utils/index'
+import { User } from '../../../../shared/index'
+
+describe('Test a video channels', function () {
+  let server: ServerInfo
+  let userInfo: User
+  let videoChannelId: number
+
+  before(async function () {
+    this.timeout(10000)
+
+    await flushTests()
+
+    server = await runServer(1)
+
+    await setAccessTokensToServers([ server ])
+  })
+
+  it('Should have one video channel (created with root)', async () => {
+    const res = await getVideoChannelsList(server.url, 0, 2)
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(1)
+  })
+
+  it('Should create another video channel', async () => {
+    const videoChannel = {
+      name: 'second video channel',
+      description: 'super video channel description'
+    }
+    await addVideoChannel(server.url, server.accessToken, videoChannel)
+  })
+
+  it('Should have two video channels when getting my information', async () => {
+    const res = await getMyUserInformation(server.url, server.accessToken)
+    userInfo = res.body
+
+    expect(userInfo.videoChannels).to.be.an('array')
+    expect(userInfo.videoChannels).to.have.lengthOf(2)
+
+    const videoChannels = userInfo.videoChannels
+    expect(videoChannels[0].name).to.equal('Default root channel')
+    expect(videoChannels[1].name).to.equal('second video channel')
+    expect(videoChannels[1].description).to.equal('super video channel description')
+  })
+
+  it('Should have two video channels when getting account channels', async () => {
+    const res = await getAccountVideoChannelsList(server.url, userInfo.account.uuid)
+
+    expect(res.body.total).to.equal(2)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(2)
+
+    const videoChannels = res.body.data
+    expect(videoChannels[0].name).to.equal('Default root channel')
+    expect(videoChannels[1].name).to.equal('second video channel')
+    expect(videoChannels[1].description).to.equal('super video channel description')
+
+    videoChannelId = videoChannels[1].id
+  })
+
+  it('Should list video channels', async () => {
+    const res = await getVideoChannelsList(server.url, 1, 1, '-name')
+
+    expect(res.body.total).to.equal(2)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(1)
+    expect(res.body.data[0].name).to.equal('Default root channel')
+  })
+
+  it('Should update video channel', async () => {
+    const videoChannelAttributes = {
+      name: 'video channel updated',
+      description: 'video channel description updated'
+    }
+
+    await updateVideoChannel(server.url, server.accessToken, videoChannelId, videoChannelAttributes)
+  })
+
+  it('Should have video channel updated', async () => {
+    const res = await getVideoChannelsList(server.url, 0, 1, '-name')
+
+    expect(res.body.total).to.equal(2)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(1)
+    expect(res.body.data[0].name).to.equal('video channel updated')
+    expect(res.body.data[0].description).to.equal('video channel description updated')
+  })
+
+  it('Should get video channel', async () => {
+    const res = await getVideoChannel(server.url, videoChannelId)
+
+    const videoChannel = res.body
+    expect(videoChannel.name).to.equal('video channel updated')
+    expect(videoChannel.description).to.equal('video channel description updated')
+  })
+
+  it('Should delete video channel', async () => {
+    await deleteVideoChannel(server.url, server.accessToken, videoChannelId)
+  })
+
+  it('Should have video channel deleted', async () => {
+    const res = await getVideoChannelsList(server.url, 0, 10)
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(1)
+    expect(res.body.data[0].name).to.equal('Default root channel')
+  })
+
+  after(async function () {
+    killallServers([ server ])
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts
new file mode 100644 (file)
index 0000000..3b6578f
--- /dev/null
@@ -0,0 +1,154 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
+import { dateIsValid, flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../utils/index'
+import {
+  addVideoCommentReply, addVideoCommentThread, getVideoCommentThreads,
+  getVideoThreadComments
+} from '../../utils/videos/video-comments'
+
+const expect = chai.expect
+
+describe('Test video comments', function () {
+  let server: ServerInfo
+  let videoId
+  let videoUUID
+  let threadId
+
+  before(async function () {
+    this.timeout(10000)
+
+    await flushTests()
+
+    server = await runServer(1)
+
+    await setAccessTokensToServers([ server ])
+
+    const res = await uploadVideo(server.url, server.accessToken, {})
+    videoUUID = res.body.video.uuid
+    videoId = res.body.video.id
+  })
+
+  it('Should not have threads on this video', async function () {
+    const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(0)
+  })
+
+  it('Should create a thread in this video', async function () {
+    const text = 'my super first comment'
+
+    const res = await addVideoCommentThread(server.url, server.accessToken, videoUUID, text)
+    const comment = res.body.comment
+
+    expect(comment.inReplyToCommentId).to.be.null
+    expect(comment.text).equal('my super first comment')
+    expect(comment.videoId).to.equal(videoId)
+    expect(comment.id).to.equal(comment.threadId)
+    expect(comment.account.name).to.equal('root')
+    expect(comment.account.host).to.equal('localhost:9001')
+    expect(comment.totalReplies).to.equal(0)
+    expect(dateIsValid(comment.createdAt as string)).to.be.true
+    expect(dateIsValid(comment.updatedAt as string)).to.be.true
+  })
+
+  it('Should list threads of this video', async function () {
+    const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(1)
+
+    const comment: VideoComment = res.body.data[0]
+    expect(comment.inReplyToCommentId).to.be.null
+    expect(comment.text).equal('my super first comment')
+    expect(comment.videoId).to.equal(videoId)
+    expect(comment.id).to.equal(comment.threadId)
+    expect(comment.account.name).to.equal('root')
+    expect(comment.account.host).to.equal('localhost:9001')
+    expect(comment.totalReplies).to.equal(0)
+    expect(dateIsValid(comment.createdAt as string)).to.be.true
+    expect(dateIsValid(comment.updatedAt as string)).to.be.true
+
+    threadId = comment.threadId
+  })
+
+  it('Should get all the thread created', async function () {
+    const res = await getVideoThreadComments(server.url, videoUUID, threadId)
+
+    const rootComment = res.body.comment
+    expect(rootComment.inReplyToCommentId).to.be.null
+    expect(rootComment.text).equal('my super first comment')
+    expect(rootComment.videoId).to.equal(videoId)
+    expect(dateIsValid(rootComment.createdAt as string)).to.be.true
+    expect(dateIsValid(rootComment.updatedAt as string)).to.be.true
+  })
+
+  it('Should create multiple replies in this thread', async function () {
+    const text1 = 'my super answer to thread 1'
+    const childCommentRes = await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text1)
+    const childCommentId = childCommentRes.body.comment.id
+
+    const text2 = 'my super answer to answer of thread 1'
+    await addVideoCommentReply(server.url, server.accessToken, videoId, childCommentId, text2)
+
+    const text3 = 'my second answer to thread 1'
+    await addVideoCommentReply(server.url, server.accessToken, videoId, threadId, text3)
+  })
+
+  it('Should get correctly the replies', async function () {
+    const res = await getVideoThreadComments(server.url, videoUUID, threadId)
+
+    const tree: VideoCommentThreadTree = res.body
+    expect(tree.comment.text).equal('my super first comment')
+    expect(tree.children).to.have.lengthOf(2)
+
+    const firstChild = tree.children[0]
+    expect(firstChild.comment.text).to.equal('my super answer to thread 1')
+    expect(firstChild.children).to.have.lengthOf(1)
+
+    const childOfFirstChild = firstChild.children[0]
+    expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
+    expect(childOfFirstChild.children).to.have.lengthOf(0)
+
+    const secondChild = tree.children[1]
+    expect(secondChild.comment.text).to.equal('my second answer to thread 1')
+    expect(secondChild.children).to.have.lengthOf(0)
+  })
+
+  it('Should create other threads', async function () {
+    const text1 = 'super thread 2'
+    await addVideoCommentThread(server.url, server.accessToken, videoUUID, text1)
+
+    const text2 = 'super thread 3'
+    await addVideoCommentThread(server.url, server.accessToken, videoUUID, text2)
+  })
+
+  it('Should list the threads', async function () {
+    const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5, 'createdAt')
+
+    expect(res.body.total).to.equal(3)
+    expect(res.body.data).to.be.an('array')
+    expect(res.body.data).to.have.lengthOf(3)
+
+    expect(res.body.data[0].text).to.equal('my super first comment')
+    expect(res.body.data[0].totalReplies).to.equal(3)
+    expect(res.body.data[1].text).to.equal('super thread 2')
+    expect(res.body.data[1].totalReplies).to.equal(0)
+    expect(res.body.data[2].text).to.equal('super thread 3')
+    expect(res.body.data[2].totalReplies).to.equal(0)
+  })
+
+  after(async function () {
+    killallServers([ server ])
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-description.ts b/server/tests/api/videos/video-description.ts
new file mode 100644 (file)
index 0000000..c298519
--- /dev/null
@@ -0,0 +1,111 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import {
+  flushAndRunMultipleServers,
+  flushTests,
+  getVideo,
+  getVideoDescription,
+  getVideosList,
+  killallServers,
+  ServerInfo,
+  setAccessTokensToServers,
+  updateVideo,
+  uploadVideo,
+  wait
+} from '../../utils/index'
+import { doubleFollow } from '../../utils/server/follows'
+
+const expect = chai.expect
+
+describe('Test video description', function () {
+  let servers: ServerInfo[] = []
+  let videoUUID = ''
+  let videoId: number
+  let longDescription = 'my super description for server 1'.repeat(50)
+
+  before(async function () {
+    this.timeout(40000)
+
+    // Run servers
+    servers = await flushAndRunMultipleServers(2)
+
+    // Get the access tokens
+    await setAccessTokensToServers(servers)
+
+    // Server 1 and server 2 follow each other
+    await doubleFollow(servers[0], servers[1])
+  })
+
+  it('Should upload video with long description', async function () {
+    this.timeout(10000)
+
+    const attributes = {
+      description: longDescription
+    }
+    await uploadVideo(servers[0].url, servers[0].accessToken, attributes)
+
+    await wait(5000)
+
+    const res = await getVideosList(servers[0].url)
+
+    videoId = res.body.data[0].id
+    videoUUID = res.body.data[0].uuid
+  })
+
+  it('Should have a truncated description on each server', async function () {
+    for (const server of servers) {
+      const res = await getVideo(server.url, videoUUID)
+      const video = res.body
+
+      // 30 characters * 6 -> 240 characters
+      const truncatedDescription = 'my super description for server 1'.repeat(7) +
+                                   'my super descrip...'
+
+      expect(video.description).to.equal(truncatedDescription)
+    }
+  })
+
+  it('Should fetch long description on each server', async function () {
+    for (const server of servers) {
+      const res = await getVideo(server.url, videoUUID)
+      const video = res.body
+
+      const res2 = await getVideoDescription(server.url, video.descriptionPath)
+      expect(res2.body.description).to.equal(longDescription)
+    }
+  })
+
+  it('Should update with a short description', async function () {
+    this.timeout(10000)
+
+    const attributes = {
+      description: 'short description'
+    }
+    await updateVideo(servers[0].url, servers[0].accessToken, videoId, attributes)
+
+    await wait(5000)
+  })
+
+  it('Should have a small description on each server', async function () {
+    for (const server of servers) {
+      const res = await getVideo(server.url, videoUUID)
+      const video = res.body
+
+      expect(video.description).to.equal('short description')
+
+      const res2 = await getVideoDescription(server.url, video.descriptionPath)
+      expect(res2.body.description).to.equal('short description')
+    }
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-privacy.ts b/server/tests/api/videos/video-privacy.ts
new file mode 100644 (file)
index 0000000..de709f8
--- /dev/null
@@ -0,0 +1,159 @@
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import 'mocha'
+import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
+import {
+  flushAndRunMultipleServers,
+  flushTests,
+  getVideosList,
+  killallServers,
+  ServerInfo,
+  setAccessTokensToServers,
+  uploadVideo,
+  wait
+} from '../../utils/index'
+import { doubleFollow } from '../../utils/server/follows'
+import { getUserAccessToken } from '../../utils/users/login'
+import { createUser } from '../../utils/users/users'
+import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../utils/videos/videos'
+
+const expect = chai.expect
+
+describe('Test video privacy', function () {
+  let servers: ServerInfo[] = []
+  let privateVideoId
+  let privateVideoUUID
+  let unlistedVideoUUID
+
+  before(async function () {
+    this.timeout(50000)
+
+    // Run servers
+    servers = await flushAndRunMultipleServers(2)
+
+    // Get the access tokens
+    await setAccessTokensToServers(servers)
+
+    // Server 1 and server 2 follow each other
+    await doubleFollow(servers[0], servers[1])
+  })
+
+  it('Should upload a private video on server 1', async function () {
+    this.timeout(10000)
+
+    const attributes = {
+      privacy: VideoPrivacy.PRIVATE
+    }
+    await uploadVideo(servers[0].url, servers[0].accessToken, attributes)
+
+    await wait(5000)
+  })
+
+  it('Should not have this private video on server 2', async function () {
+    const res = await getVideosList(servers[1].url)
+
+    expect(res.body.total).to.equal(0)
+    expect(res.body.data).to.have.lengthOf(0)
+  })
+
+  it('Should list my (private) videos', async function () {
+    const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 1)
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.have.lengthOf(1)
+
+    privateVideoId = res.body.data[0].id
+    privateVideoUUID = res.body.data[0].uuid
+  })
+
+  it('Should not be able to watch this video with non authenticated user', async function () {
+    await getVideo(servers[0].url, privateVideoUUID, 401)
+  })
+
+  it('Should not be able to watch this private video with another user', async function () {
+    const user = {
+      username: 'hello',
+      password: 'super password'
+    }
+    await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
+
+    const token = await getUserAccessToken(servers[0], user)
+    await getVideoWithToken(servers[0].url, token, privateVideoUUID, 403)
+  })
+
+  it('Should be able to watch this video with the correct user', async function () {
+    await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID)
+  })
+
+  it('Should upload an unlisted video on server 2', async function () {
+    this.timeout(30000)
+
+    const attributes = {
+      name: 'unlisted video',
+      privacy: VideoPrivacy.UNLISTED
+    }
+    await uploadVideo(servers[1].url, servers[1].accessToken, attributes)
+
+    // Server 2 has transcoding enabled
+    await wait(10000)
+  })
+
+  it('Should not have this unlisted video listed on server 1 and 2', async function () {
+    for (const server of servers) {
+      const res = await getVideosList(server.url)
+
+      expect(res.body.total).to.equal(0)
+      expect(res.body.data).to.have.lengthOf(0)
+    }
+  })
+
+  it('Should list my (unlisted) videos', async function () {
+    const res = await getMyVideos(servers[1].url, servers[1].accessToken, 0, 1)
+
+    expect(res.body.total).to.equal(1)
+    expect(res.body.data).to.have.lengthOf(1)
+
+    unlistedVideoUUID = res.body.data[0].uuid
+  })
+
+  it('Should be able to get this unlisted video', async function () {
+    for (const server of servers) {
+      const res = await getVideo(server.url, unlistedVideoUUID)
+
+      expect(res.body.name).to.equal('unlisted video')
+    }
+  })
+
+  it('Should update the private video to public on server 1', async function () {
+    this.timeout(10000)
+
+    const attribute = {
+      name: 'super video public',
+      privacy: VideoPrivacy.PUBLIC
+    }
+
+    await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute)
+
+    await wait(5000)
+  })
+
+  it('Should have this new public video listed on server 1 and 2', async function () {
+    for (const server of servers) {
+      const res = await getVideosList(server.url)
+
+      expect(res.body.total).to.equal(1)
+      expect(res.body.data).to.have.lengthOf(1)
+      expect(res.body.data[0].name).to.equal('super video public')
+    }
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts
new file mode 100644 (file)
index 0000000..27927a5
--- /dev/null
@@ -0,0 +1,97 @@
+/* tslint:disable:no-unused-expression */
+
+import 'mocha'
+import * as chai from 'chai'
+const expect = chai.expect
+
+import {
+  ServerInfo,
+  flushTests,
+  uploadVideo,
+  getVideosList,
+  wait,
+  setAccessTokensToServers,
+  flushAndRunMultipleServers,
+  killallServers,
+  webtorrentAdd,
+  getVideo
+} from '../../utils/index'
+
+describe('Test video transcoding', function () {
+  let servers: ServerInfo[] = []
+
+  before(async function () {
+    this.timeout(10000)
+
+    // Run servers
+    servers = await flushAndRunMultipleServers(2)
+
+    await setAccessTokensToServers(servers)
+  })
+
+  it('Should not transcode video on server 1', async function () {
+    this.timeout(60000)
+
+    const videoAttributes = {
+      name: 'my super name for server 1',
+      description: 'my super description for server 1',
+      fixture: 'video_short.webm'
+    }
+    await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
+
+    await wait(10000)
+
+    const res = await getVideosList(servers[0].url)
+    const video = res.body.data[0]
+
+    const res2 = await getVideo(servers[0].url, video.id)
+    const videoDetails = res2.body
+    expect(videoDetails.files).to.have.lengthOf(1)
+
+    const magnetUri = videoDetails.files[0].magnetUri
+    expect(magnetUri).to.match(/\.webm/)
+
+    const torrent = await webtorrentAdd(magnetUri)
+    expect(torrent.files).to.be.an('array')
+    expect(torrent.files.length).to.equal(1)
+    expect(torrent.files[0].path).match(/\.webm$/)
+  })
+
+  it('Should transcode video on server 2', async function () {
+    this.timeout(60000)
+
+    const videoAttributes = {
+      name: 'my super name for server 2',
+      description: 'my super description for server 2',
+      fixture: 'video_short.webm'
+    }
+    await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes)
+
+    await wait(20000)
+
+    const res = await getVideosList(servers[1].url)
+
+    const video = res.body.data[0]
+    const res2 = await getVideo(servers[1].url, video.id)
+    const videoDetails = res2.body
+
+    expect(videoDetails.files).to.have.lengthOf(4)
+
+    const magnetUri = videoDetails.files[0].magnetUri
+    expect(magnetUri).to.match(/\.mp4/)
+
+    const torrent = await webtorrentAdd(magnetUri)
+    expect(torrent.files).to.be.an('array')
+    expect(torrent.files.length).to.equal(1)
+    expect(torrent.files[0].path).match(/\.mp4$/)
+  })
+
+  after(async function () {
+    killallServers(servers)
+
+    // Keep the logs if the test failed
+    if (this['ok']) {
+      await flushTests()
+    }
+  })
+})
diff --git a/server/tests/utils/activitypub.ts b/server/tests/utils/activitypub.ts
deleted file mode 100644 (file)
index cf3c1c3..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as request from 'supertest'
-
-function makeActivityPubGetRequest (url: string, path: string) {
-  return request(url)
-    .get(path)
-    .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  makeActivityPubGetRequest
-}
diff --git a/server/tests/utils/cli.ts b/server/tests/utils/cli.ts
deleted file mode 100644 (file)
index 4098fdf..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-import { exec } from 'child_process'
-
-import { ServerInfo } from './servers'
-
-function getEnvCli (server?: ServerInfo) {
-  return `NODE_ENV=test NODE_APP_INSTANCE=${server.serverNumber}`
-}
-
-async function execCLI (command: string) {
-  return new Promise<string>((res, rej) => {
-    exec(command, (err, stdout, stderr) => {
-      if (err) return rej(err)
-
-      return res(stdout)
-    })
-  })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  execCLI,
-  getEnvCli
-}
diff --git a/server/tests/utils/cli/cli.ts b/server/tests/utils/cli/cli.ts
new file mode 100644 (file)
index 0000000..54d05e9
--- /dev/null
@@ -0,0 +1,24 @@
+import { exec } from 'child_process'
+
+import { ServerInfo } from '../server/servers'
+
+function getEnvCli (server?: ServerInfo) {
+  return `NODE_ENV=test NODE_APP_INSTANCE=${server.serverNumber}`
+}
+
+async function execCLI (command: string) {
+  return new Promise<string>((res, rej) => {
+    exec(command, (err, stdout, stderr) => {
+      if (err) return rej(err)
+
+      return res(stdout)
+    })
+  })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  execCLI,
+  getEnvCli
+}
diff --git a/server/tests/utils/clients.ts b/server/tests/utils/clients.ts
deleted file mode 100644 (file)
index a8c5b51..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as request from 'supertest'
-
-function getClient (url: string) {
-  const path = '/api/v1/oauth-clients/local'
-
-  return request(url)
-          .get(path)
-          .set('Accept', 'application/json')
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getClient
-}
diff --git a/server/tests/utils/config.ts b/server/tests/utils/config.ts
deleted file mode 100644 (file)
index d09c19c..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as request from 'supertest'
-
-function getConfig (url: string) {
-  const path = '/api/v1/config'
-
-  return request(url)
-          .get(path)
-          .set('Accept', 'application/json')
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getConfig
-}
diff --git a/server/tests/utils/follows.ts b/server/tests/utils/follows.ts
deleted file mode 100644 (file)
index a9f798b..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-import * as request from 'supertest'
-import { wait } from './miscs'
-import { ServerInfo } from './servers'
-
-function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string) {
-  const path = '/api/v1/server/followers'
-
-  return request(url)
-    .get(path)
-    .query({ start })
-    .query({ count })
-    .query({ sort })
-    .set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string) {
-  const path = '/api/v1/server/following'
-
-  return request(url)
-    .get(path)
-    .query({ start })
-    .query({ count })
-    .query({ sort })
-    .set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-async function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) {
-  const path = '/api/v1/server/following'
-
-  const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
-  const res = await request(follower)
-    .post(path)
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + accessToken)
-    .send({ 'hosts': followingHosts })
-    .expect(expectedStatus)
-
-  return res
-}
-
-async function unfollow (url: string, accessToken: string, target: ServerInfo, expectedStatus = 204) {
-  const path = '/api/v1/server/following/' + target.host
-
-  const res = await request(url)
-    .delete(path)
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + accessToken)
-    .expect(expectedStatus)
-
-  return res
-}
-
-async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
-  await Promise.all([
-    follow(server1.url, [ server2.url ], server1.accessToken),
-    follow(server2.url, [ server1.url ], server2.accessToken)
-  ])
-
-  // Wait request propagation
-  await wait(10000)
-
-  return true
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getFollowersListPaginationAndSort,
-  getFollowingListPaginationAndSort,
-  unfollow,
-  follow,
-  doubleFollow
-}
index b918ee83d21ed09d8a9bdc2ce69e99f5c81efc7b..d7789e517334ffed21d3bb645683fbc13015271b 100644 (file)
@@ -1,15 +1,15 @@
-export * from './activitypub'
-export * from './cli'
-export * from './clients'
-export * from './config'
-export * from './login'
-export * from './miscs'
-export * from './follows'
-export * from './requests'
-export * from './servers'
-export * from './services'
-export * from './users'
-export * from './video-abuses'
-export * from './video-blacklist'
-export * from './video-channels'
-export * from './videos'
+export * from './server/activitypub'
+export * from './cli/cli'
+export * from './server/clients'
+export * from './server/config'
+export * from './users/login'
+export * from './miscs/miscs'
+export * from './server/follows'
+export * from './requests/requests'
+export * from './server/servers'
+export * from './videos/services'
+export * from './users/users'
+export * from './videos/video-abuses'
+export * from './videos/video-blacklist'
+export * from './videos/video-channels'
+export * from './videos/videos'
diff --git a/server/tests/utils/jobs.ts b/server/tests/utils/jobs.ts
deleted file mode 100644 (file)
index 0a8c515..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-import * as request from 'supertest'
-
-function getJobsList (url: string, accessToken: string) {
-  const path = '/api/v1/jobs'
-
-  return request(url)
-          .get(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-function getJobsListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string) {
-  const path = '/api/v1/jobs'
-
-  return request(url)
-          .get(path)
-          .query({ start })
-          .query({ count })
-          .query({ sort })
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getJobsList,
-  getJobsListPaginationAndSort
-}
diff --git a/server/tests/utils/login.ts b/server/tests/utils/login.ts
deleted file mode 100644 (file)
index 22d37c4..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-import * as request from 'supertest'
-
-import { ServerInfo } from './servers'
-
-type Client = { id: string, secret: string }
-type User = { username: string, password: string }
-type Server = { url: string, client: Client, user: User }
-
-function login (url: string, client: Client, user: User, expectedStatus = 200) {
-  const path = '/api/v1/users/token'
-
-  const body = {
-    client_id: client.id,
-    client_secret: client.secret,
-    username: user.username,
-    password: user.password,
-    response_type: 'code',
-    grant_type: 'password',
-    scope: 'upload'
-  }
-
-  return request(url)
-          .post(path)
-          .type('form')
-          .send(body)
-          .expect(expectedStatus)
-}
-
-async function loginAndGetAccessToken (server: Server) {
-  const res = await login(server.url, server.client, server.user, 200)
-
-  return res.body.access_token as string
-}
-
-async function getUserAccessToken (server: Server, user: User) {
-  const res = await login(server.url, server.client, user, 200)
-
-  return res.body.access_token as string
-}
-
-function setAccessTokensToServers (servers: ServerInfo[]) {
-  const tasks: Promise<any>[] = []
-
-  for (const server of servers) {
-    const p = loginAndGetAccessToken(server).then(t => server.accessToken = t)
-    tasks.push(p)
-  }
-
-  return Promise.all(tasks)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  login,
-  loginAndGetAccessToken,
-  getUserAccessToken,
-  setAccessTokensToServers
-}
diff --git a/server/tests/utils/miscs.ts b/server/tests/utils/miscs.ts
deleted file mode 100644 (file)
index 424b0db..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-import * as WebTorrent from 'webtorrent'
-import { readFile, readdir } from 'fs'
-
-let webtorrent = new WebTorrent()
-
-function readFilePromise (path: string) {
-  return new Promise<Buffer>((res, rej) => {
-    readFile(path, (err, data) => {
-      if (err) return rej(err)
-
-      return res(data)
-    })
-  })
-}
-
-function readdirPromise (path: string) {
-  return new Promise<string[]>((res, rej) => {
-    readdir(path, (err, files) => {
-      if (err) return rej(err)
-
-      return res(files)
-    })
-  })
-}
-
-  // Default interval -> 2 minutes
-function dateIsValid (dateString: string, interval = 120000) {
-  const dateToCheck = new Date(dateString)
-  const now = new Date()
-
-  return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
-}
-
-function wait (milliseconds: number) {
-  return new Promise(resolve => setTimeout(resolve, milliseconds))
-}
-
-function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
-  if (refreshWebTorrent === true) webtorrent = new WebTorrent()
-
-  return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  readFilePromise,
-  readdirPromise,
-  dateIsValid,
-  wait,
-  webtorrentAdd
-}
diff --git a/server/tests/utils/miscs/miscs.ts b/server/tests/utils/miscs/miscs.ts
new file mode 100644 (file)
index 0000000..424b0db
--- /dev/null
@@ -0,0 +1,52 @@
+import * as WebTorrent from 'webtorrent'
+import { readFile, readdir } from 'fs'
+
+let webtorrent = new WebTorrent()
+
+function readFilePromise (path: string) {
+  return new Promise<Buffer>((res, rej) => {
+    readFile(path, (err, data) => {
+      if (err) return rej(err)
+
+      return res(data)
+    })
+  })
+}
+
+function readdirPromise (path: string) {
+  return new Promise<string[]>((res, rej) => {
+    readdir(path, (err, files) => {
+      if (err) return rej(err)
+
+      return res(files)
+    })
+  })
+}
+
+  // Default interval -> 2 minutes
+function dateIsValid (dateString: string, interval = 120000) {
+  const dateToCheck = new Date(dateString)
+  const now = new Date()
+
+  return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
+}
+
+function wait (milliseconds: number) {
+  return new Promise(resolve => setTimeout(resolve, milliseconds))
+}
+
+function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
+  if (refreshWebTorrent === true) webtorrent = new WebTorrent()
+
+  return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  readFilePromise,
+  readdirPromise,
+  dateIsValid,
+  wait,
+  webtorrentAdd
+}
diff --git a/server/tests/utils/requests.ts b/server/tests/utils/requests.ts
deleted file mode 100644 (file)
index 52b7a4c..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-import * as request from 'supertest'
-
-function makeGetRequest (url: string, path: string) {
-  return request(url)
-    .get(path)
-    .set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function makePostUploadRequest (options: {
-  url: string,
-  path: string,
-  token: string,
-  fields: { [ fieldName: string ]: any },
-  attaches: { [ attachName: string ]: any },
-  statusCodeExpected?: number
-}) {
-  if (!options.statusCodeExpected) options.statusCodeExpected = 400
-
-  const req = request(options.url)
-                .post(options.path)
-                .set('Accept', 'application/json')
-
-  if (options.token) req.set('Authorization', 'Bearer ' + options.token)
-
-  Object.keys(options.fields).forEach(field => {
-    const value = options.fields[field]
-
-    if (Array.isArray(value)) {
-      for (let i = 0; i < value.length; i++) {
-        req.field(field + '[' + i + ']', value[i])
-      }
-    } else {
-      req.field(field, value)
-    }
-  })
-
-  Object.keys(options.attaches).forEach(attach => {
-    const value = options.attaches[attach]
-    req.attach(attach, value)
-  })
-
-  return req.expect(options.statusCodeExpected)
-}
-
-function makePostBodyRequest (options: {
-  url: string,
-  path: string,
-  token?: string,
-  fields: { [ fieldName: string ]: any },
-  statusCodeExpected?: number
-}) {
-  if (!options.statusCodeExpected) options.statusCodeExpected = 400
-
-  const req = request(options.url)
-                .post(options.path)
-                .set('Accept', 'application/json')
-
-  if (options.token) req.set('Authorization', 'Bearer ' + options.token)
-
-  return req.send(options.fields)
-            .expect(options.statusCodeExpected)
-}
-
-function makePutBodyRequest (options: {
-  url: string,
-  path: string,
-  token: string,
-  fields: { [ fieldName: string ]: any },
-  statusCodeExpected?: number
-}) {
-  if (!options.statusCodeExpected) options.statusCodeExpected = 400
-
-  const req = request(options.url)
-                .put(options.path)
-                .set('Accept', 'application/json')
-
-  if (options.token) req.set('Authorization', 'Bearer ' + options.token)
-
-  return req.send(options.fields)
-            .expect(options.statusCodeExpected)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  makeGetRequest,
-  makePostUploadRequest,
-  makePostBodyRequest,
-  makePutBodyRequest
-}
diff --git a/server/tests/utils/requests/requests.ts b/server/tests/utils/requests/requests.ts
new file mode 100644 (file)
index 0000000..52b7a4c
--- /dev/null
@@ -0,0 +1,92 @@
+import * as request from 'supertest'
+
+function makeGetRequest (url: string, path: string) {
+  return request(url)
+    .get(path)
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function makePostUploadRequest (options: {
+  url: string,
+  path: string,
+  token: string,
+  fields: { [ fieldName: string ]: any },
+  attaches: { [ attachName: string ]: any },
+  statusCodeExpected?: number
+}) {
+  if (!options.statusCodeExpected) options.statusCodeExpected = 400
+
+  const req = request(options.url)
+                .post(options.path)
+                .set('Accept', 'application/json')
+
+  if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+
+  Object.keys(options.fields).forEach(field => {
+    const value = options.fields[field]
+
+    if (Array.isArray(value)) {
+      for (let i = 0; i < value.length; i++) {
+        req.field(field + '[' + i + ']', value[i])
+      }
+    } else {
+      req.field(field, value)
+    }
+  })
+
+  Object.keys(options.attaches).forEach(attach => {
+    const value = options.attaches[attach]
+    req.attach(attach, value)
+  })
+
+  return req.expect(options.statusCodeExpected)
+}
+
+function makePostBodyRequest (options: {
+  url: string,
+  path: string,
+  token?: string,
+  fields: { [ fieldName: string ]: any },
+  statusCodeExpected?: number
+}) {
+  if (!options.statusCodeExpected) options.statusCodeExpected = 400
+
+  const req = request(options.url)
+                .post(options.path)
+                .set('Accept', 'application/json')
+
+  if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+
+  return req.send(options.fields)
+            .expect(options.statusCodeExpected)
+}
+
+function makePutBodyRequest (options: {
+  url: string,
+  path: string,
+  token: string,
+  fields: { [ fieldName: string ]: any },
+  statusCodeExpected?: number
+}) {
+  if (!options.statusCodeExpected) options.statusCodeExpected = 400
+
+  const req = request(options.url)
+                .put(options.path)
+                .set('Accept', 'application/json')
+
+  if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+
+  return req.send(options.fields)
+            .expect(options.statusCodeExpected)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  makeGetRequest,
+  makePostUploadRequest,
+  makePostBodyRequest,
+  makePutBodyRequest
+}
diff --git a/server/tests/utils/server/activitypub.ts b/server/tests/utils/server/activitypub.ts
new file mode 100644 (file)
index 0000000..cf3c1c3
--- /dev/null
@@ -0,0 +1,15 @@
+import * as request from 'supertest'
+
+function makeActivityPubGetRequest (url: string, path: string) {
+  return request(url)
+    .get(path)
+    .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  makeActivityPubGetRequest
+}
diff --git a/server/tests/utils/server/clients.ts b/server/tests/utils/server/clients.ts
new file mode 100644 (file)
index 0000000..a8c5b51
--- /dev/null
@@ -0,0 +1,17 @@
+import * as request from 'supertest'
+
+function getClient (url: string) {
+  const path = '/api/v1/oauth-clients/local'
+
+  return request(url)
+          .get(path)
+          .set('Accept', 'application/json')
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getClient
+}
diff --git a/server/tests/utils/server/config.ts b/server/tests/utils/server/config.ts
new file mode 100644 (file)
index 0000000..d09c19c
--- /dev/null
@@ -0,0 +1,17 @@
+import * as request from 'supertest'
+
+function getConfig (url: string) {
+  const path = '/api/v1/config'
+
+  return request(url)
+          .get(path)
+          .set('Accept', 'application/json')
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getConfig
+}
diff --git a/server/tests/utils/server/follows.ts b/server/tests/utils/server/follows.ts
new file mode 100644 (file)
index 0000000..82e8917
--- /dev/null
@@ -0,0 +1,77 @@
+import * as request from 'supertest'
+import { wait } from '../miscs/miscs'
+import { ServerInfo } from './servers'
+
+function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string) {
+  const path = '/api/v1/server/followers'
+
+  return request(url)
+    .get(path)
+    .query({ start })
+    .query({ count })
+    .query({ sort })
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string) {
+  const path = '/api/v1/server/following'
+
+  return request(url)
+    .get(path)
+    .query({ start })
+    .query({ count })
+    .query({ sort })
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+async function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) {
+  const path = '/api/v1/server/following'
+
+  const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
+  const res = await request(follower)
+    .post(path)
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + accessToken)
+    .send({ 'hosts': followingHosts })
+    .expect(expectedStatus)
+
+  return res
+}
+
+async function unfollow (url: string, accessToken: string, target: ServerInfo, expectedStatus = 204) {
+  const path = '/api/v1/server/following/' + target.host
+
+  const res = await request(url)
+    .delete(path)
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + accessToken)
+    .expect(expectedStatus)
+
+  return res
+}
+
+async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
+  await Promise.all([
+    follow(server1.url, [ server2.url ], server1.accessToken),
+    follow(server2.url, [ server1.url ], server2.accessToken)
+  ])
+
+  // Wait request propagation
+  await wait(10000)
+
+  return true
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getFollowersListPaginationAndSort,
+  getFollowingListPaginationAndSort,
+  unfollow,
+  follow,
+  doubleFollow
+}
diff --git a/server/tests/utils/server/jobs.ts b/server/tests/utils/server/jobs.ts
new file mode 100644 (file)
index 0000000..0a8c515
--- /dev/null
@@ -0,0 +1,33 @@
+import * as request from 'supertest'
+
+function getJobsList (url: string, accessToken: string) {
+  const path = '/api/v1/jobs'
+
+  return request(url)
+          .get(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+function getJobsListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string) {
+  const path = '/api/v1/jobs'
+
+  return request(url)
+          .get(path)
+          .query({ start })
+          .query({ count })
+          .query({ sort })
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getJobsList,
+  getJobsListPaginationAndSort
+}
diff --git a/server/tests/utils/server/servers.ts b/server/tests/utils/server/servers.ts
new file mode 100644 (file)
index 0000000..8340fbc
--- /dev/null
@@ -0,0 +1,162 @@
+import { ChildProcess, exec, fork } from 'child_process'
+import { join } from 'path'
+
+interface ServerInfo {
+  app: ChildProcess,
+  url: string
+  host: string
+  serverNumber: number
+
+  client: {
+    id: string,
+    secret: string
+  }
+
+  user: {
+    username: string,
+    password: string,
+    email?: string
+  }
+
+  accessToken?: string
+
+  video?: {
+    id: number
+    uuid: string
+    name: string
+    accountName: string
+  }
+
+  remoteVideo?: {
+    id: number
+    uuid: string
+  }
+}
+
+function flushAndRunMultipleServers (totalServers) {
+  let apps = []
+  let i = 0
+
+  return new Promise<ServerInfo[]>(res => {
+    function anotherServerDone (serverNumber, app) {
+      apps[serverNumber - 1] = app
+      i++
+      if (i === totalServers) {
+        return res(apps)
+      }
+    }
+
+    flushTests()
+      .then(() => {
+        for (let j = 1; j <= totalServers; j++) {
+          // For the virtual buffer
+          setTimeout(() => {
+            runServer(j).then(app => anotherServerDone(j, app))
+          }, 1000 * (j - 1))
+        }
+      })
+  })
+}
+
+function flushTests () {
+  return new Promise<void>((res, rej) => {
+    return exec('npm run clean:server:test', err => {
+      if (err) return rej(err)
+
+      return res()
+    })
+  })
+}
+
+function runServer (serverNumber: number, configOverride?: Object) {
+  const server: ServerInfo = {
+    app: null,
+    serverNumber: serverNumber,
+    url: `http://localhost:${9000 + serverNumber}`,
+    host: `localhost:${9000 + serverNumber}`,
+    client: {
+      id: null,
+      secret: null
+    },
+    user: {
+      username: null,
+      password: null
+    }
+  }
+
+  // These actions are async so we need to be sure that they have both been done
+  const serverRunString = {
+    'Server listening on port': false
+  }
+  const key = 'Database peertube_test' + serverNumber + ' is ready'
+  serverRunString[key] = false
+
+  const regexps = {
+    client_id: 'Client id: (.+)',
+    client_secret: 'Client secret: (.+)',
+    user_username: 'Username: (.+)',
+    user_password: 'User password: (.+)'
+  }
+
+  // Share the environment
+  const env = Object.create(process.env)
+  env['NODE_ENV'] = 'test'
+  env['NODE_APP_INSTANCE'] = serverNumber.toString()
+
+  if (configOverride !== undefined) {
+    env['NODE_CONFIG'] = JSON.stringify(configOverride)
+  }
+
+  const options = {
+    silent: true,
+    env: env,
+    detached: true
+  }
+
+  return new Promise<ServerInfo>(res => {
+    server.app = fork(join(__dirname, '..', '..', '..', 'dist', 'server.js'), [], options)
+    server.app.stdout.on('data', function onStdout (data) {
+      let dontContinue = false
+
+      // Capture things if we want to
+      for (const key of Object.keys(regexps)) {
+        const regexp = regexps[key]
+        const matches = data.toString().match(regexp)
+        if (matches !== null) {
+          if (key === 'client_id') server.client.id = matches[1]
+          else if (key === 'client_secret') server.client.secret = matches[1]
+          else if (key === 'user_username') server.user.username = matches[1]
+          else if (key === 'user_password') server.user.password = matches[1]
+        }
+      }
+
+      // Check if all required sentences are here
+      for (const key of Object.keys(serverRunString)) {
+        if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
+        if (serverRunString[key] === false) dontContinue = true
+      }
+
+      // If no, there is maybe one thing not already initialized (client/user credentials generation...)
+      if (dontContinue === true) return
+
+      server.app.stdout.removeListener('data', onStdout)
+      res(server)
+    })
+  })
+}
+
+function killallServers (servers: ServerInfo[]) {
+  for (const server of servers) {
+    process.kill(-server.app.pid)
+  }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  ServerInfo,
+  flushAndRunMultipleServers,
+  flushTests,
+  runServer,
+  killallServers
+}
diff --git a/server/tests/utils/servers.ts b/server/tests/utils/servers.ts
deleted file mode 100644 (file)
index 8340fbc..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-import { ChildProcess, exec, fork } from 'child_process'
-import { join } from 'path'
-
-interface ServerInfo {
-  app: ChildProcess,
-  url: string
-  host: string
-  serverNumber: number
-
-  client: {
-    id: string,
-    secret: string
-  }
-
-  user: {
-    username: string,
-    password: string,
-    email?: string
-  }
-
-  accessToken?: string
-
-  video?: {
-    id: number
-    uuid: string
-    name: string
-    accountName: string
-  }
-
-  remoteVideo?: {
-    id: number
-    uuid: string
-  }
-}
-
-function flushAndRunMultipleServers (totalServers) {
-  let apps = []
-  let i = 0
-
-  return new Promise<ServerInfo[]>(res => {
-    function anotherServerDone (serverNumber, app) {
-      apps[serverNumber - 1] = app
-      i++
-      if (i === totalServers) {
-        return res(apps)
-      }
-    }
-
-    flushTests()
-      .then(() => {
-        for (let j = 1; j <= totalServers; j++) {
-          // For the virtual buffer
-          setTimeout(() => {
-            runServer(j).then(app => anotherServerDone(j, app))
-          }, 1000 * (j - 1))
-        }
-      })
-  })
-}
-
-function flushTests () {
-  return new Promise<void>((res, rej) => {
-    return exec('npm run clean:server:test', err => {
-      if (err) return rej(err)
-
-      return res()
-    })
-  })
-}
-
-function runServer (serverNumber: number, configOverride?: Object) {
-  const server: ServerInfo = {
-    app: null,
-    serverNumber: serverNumber,
-    url: `http://localhost:${9000 + serverNumber}`,
-    host: `localhost:${9000 + serverNumber}`,
-    client: {
-      id: null,
-      secret: null
-    },
-    user: {
-      username: null,
-      password: null
-    }
-  }
-
-  // These actions are async so we need to be sure that they have both been done
-  const serverRunString = {
-    'Server listening on port': false
-  }
-  const key = 'Database peertube_test' + serverNumber + ' is ready'
-  serverRunString[key] = false
-
-  const regexps = {
-    client_id: 'Client id: (.+)',
-    client_secret: 'Client secret: (.+)',
-    user_username: 'Username: (.+)',
-    user_password: 'User password: (.+)'
-  }
-
-  // Share the environment
-  const env = Object.create(process.env)
-  env['NODE_ENV'] = 'test'
-  env['NODE_APP_INSTANCE'] = serverNumber.toString()
-
-  if (configOverride !== undefined) {
-    env['NODE_CONFIG'] = JSON.stringify(configOverride)
-  }
-
-  const options = {
-    silent: true,
-    env: env,
-    detached: true
-  }
-
-  return new Promise<ServerInfo>(res => {
-    server.app = fork(join(__dirname, '..', '..', '..', 'dist', 'server.js'), [], options)
-    server.app.stdout.on('data', function onStdout (data) {
-      let dontContinue = false
-
-      // Capture things if we want to
-      for (const key of Object.keys(regexps)) {
-        const regexp = regexps[key]
-        const matches = data.toString().match(regexp)
-        if (matches !== null) {
-          if (key === 'client_id') server.client.id = matches[1]
-          else if (key === 'client_secret') server.client.secret = matches[1]
-          else if (key === 'user_username') server.user.username = matches[1]
-          else if (key === 'user_password') server.user.password = matches[1]
-        }
-      }
-
-      // Check if all required sentences are here
-      for (const key of Object.keys(serverRunString)) {
-        if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
-        if (serverRunString[key] === false) dontContinue = true
-      }
-
-      // If no, there is maybe one thing not already initialized (client/user credentials generation...)
-      if (dontContinue === true) return
-
-      server.app.stdout.removeListener('data', onStdout)
-      res(server)
-    })
-  })
-}
-
-function killallServers (servers: ServerInfo[]) {
-  for (const server of servers) {
-    process.kill(-server.app.pid)
-  }
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  ServerInfo,
-  flushAndRunMultipleServers,
-  flushTests,
-  runServer,
-  killallServers
-}
diff --git a/server/tests/utils/services.ts b/server/tests/utils/services.ts
deleted file mode 100644 (file)
index 1a53dd4..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as request from 'supertest'
-
-function getOEmbed (url: string, oembedUrl: string, format?: string, maxHeight?: number, maxWidth?: number) {
-  const path = '/services/oembed'
-  const query = {
-    url: oembedUrl,
-    format,
-    maxheight: maxHeight,
-    maxwidth: maxWidth
-  }
-
-  return request(url)
-          .get(path)
-          .query(query)
-          .set('Accept', 'application/json')
-          .expect(200)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getOEmbed
-}
diff --git a/server/tests/utils/users.ts b/server/tests/utils/users.ts
deleted file mode 100644 (file)
index a37d84a..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-import * as request from 'supertest'
-
-import { UserRole } from '../../../shared'
-
-function createUser (
-  url: string,
-  accessToken: string,
-  username: string,
-  password: string,
-  videoQuota = 1000000,
-  role: UserRole = UserRole.USER,
-  specialStatus = 204
-) {
-  const path = '/api/v1/users'
-  const body = {
-    username,
-    password,
-    role,
-    email: username + '@example.com',
-    videoQuota
-  }
-
-  return request(url)
-          .post(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .send(body)
-          .expect(specialStatus)
-}
-
-function registerUser (url: string, username: string, password: string, specialStatus = 204) {
-  const path = '/api/v1/users/register'
-  const body = {
-    username,
-    password,
-    email: username + '@example.com'
-  }
-
-  return request(url)
-          .post(path)
-          .set('Accept', 'application/json')
-          .send(body)
-          .expect(specialStatus)
-}
-
-function getMyUserInformation (url: string, accessToken: string) {
-  const path = '/api/v1/users/me'
-
-  return request(url)
-          .get(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-function getUserInformation (url: string, accessToken: string, userId: number) {
-  const path = '/api/v1/users/' + userId
-
-  return request(url)
-    .get(path)
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + accessToken)
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function getUserVideoRating (url: string, accessToken: string, videoId: number) {
-  const path = '/api/v1/users/me/videos/' + videoId + '/rating'
-
-  return request(url)
-          .get(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-function getUsersList (url: string, accessToken: string) {
-  const path = '/api/v1/users'
-
-  return request(url)
-          .get(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string) {
-  const path = '/api/v1/users'
-
-  return request(url)
-          .get(path)
-          .query({ start })
-          .query({ count })
-          .query({ sort })
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-function removeUser (url: string, userId: number, accessToken: string, expectedStatus = 204) {
-  const path = '/api/v1/users'
-
-  return request(url)
-          .delete(path + '/' + userId)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(expectedStatus)
-}
-
-function updateMyUser (url: string, accessToken: string, newPassword: string, displayNSFW?: boolean,
-  email?: string, autoPlayVideo?: boolean) {
-  const path = '/api/v1/users/me'
-
-  const toSend = {}
-  if (newPassword !== undefined && newPassword !== null) toSend['password'] = newPassword
-  if (displayNSFW !== undefined && displayNSFW !== null) toSend['displayNSFW'] = displayNSFW
-  if (autoPlayVideo !== undefined && autoPlayVideo !== null) toSend['autoPlayVideo'] = autoPlayVideo
-  if (email !== undefined && email !== null) toSend['email'] = email
-
-  return request(url)
-    .put(path)
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + accessToken)
-    .send(toSend)
-    .expect(204)
-}
-
-function updateUser (url: string, userId: number, accessToken: string, email: string, videoQuota: number, role: UserRole) {
-  const path = '/api/v1/users/' + userId
-
-  const toSend = {}
-  if (email !== undefined && email !== null) toSend['email'] = email
-  if (videoQuota !== undefined && videoQuota !== null) toSend['videoQuota'] = videoQuota
-  if (role !== undefined && role !== null) toSend['role'] = role
-
-  return request(url)
-          .put(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .send(toSend)
-          .expect(204)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  createUser,
-  registerUser,
-  getMyUserInformation,
-  getUserVideoRating,
-  getUsersList,
-  getUsersListPaginationAndSort,
-  removeUser,
-  updateUser,
-  updateMyUser,
-  getUserInformation
-}
diff --git a/server/tests/utils/users/login.ts b/server/tests/utils/users/login.ts
new file mode 100644 (file)
index 0000000..855c482
--- /dev/null
@@ -0,0 +1,59 @@
+import * as request from 'supertest'
+
+import { ServerInfo } from '../server/servers'
+
+type Client = { id: string, secret: string }
+type User = { username: string, password: string }
+type Server = { url: string, client: Client, user: User }
+
+function login (url: string, client: Client, user: User, expectedStatus = 200) {
+  const path = '/api/v1/users/token'
+
+  const body = {
+    client_id: client.id,
+    client_secret: client.secret,
+    username: user.username,
+    password: user.password,
+    response_type: 'code',
+    grant_type: 'password',
+    scope: 'upload'
+  }
+
+  return request(url)
+          .post(path)
+          .type('form')
+          .send(body)
+          .expect(expectedStatus)
+}
+
+async function loginAndGetAccessToken (server: Server) {
+  const res = await login(server.url, server.client, server.user, 200)
+
+  return res.body.access_token as string
+}
+
+async function getUserAccessToken (server: Server, user: User) {
+  const res = await login(server.url, server.client, user, 200)
+
+  return res.body.access_token as string
+}
+
+function setAccessTokensToServers (servers: ServerInfo[]) {
+  const tasks: Promise<any>[] = []
+
+  for (const server of servers) {
+    const p = loginAndGetAccessToken(server).then(t => server.accessToken = t)
+    tasks.push(p)
+  }
+
+  return Promise.all(tasks)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  login,
+  loginAndGetAccessToken,
+  getUserAccessToken,
+  setAccessTokensToServers
+}
diff --git a/server/tests/utils/users/users.ts b/server/tests/utils/users/users.ts
new file mode 100644 (file)
index 0000000..bd8d7ab
--- /dev/null
@@ -0,0 +1,161 @@
+import * as request from 'supertest'
+
+import { UserRole } from '../../../../shared/index'
+
+function createUser (
+  url: string,
+  accessToken: string,
+  username: string,
+  password: string,
+  videoQuota = 1000000,
+  role: UserRole = UserRole.USER,
+  specialStatus = 204
+) {
+  const path = '/api/v1/users'
+  const body = {
+    username,
+    password,
+    role,
+    email: username + '@example.com',
+    videoQuota
+  }
+
+  return request(url)
+          .post(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .send(body)
+          .expect(specialStatus)
+}
+
+function registerUser (url: string, username: string, password: string, specialStatus = 204) {
+  const path = '/api/v1/users/register'
+  const body = {
+    username,
+    password,
+    email: username + '@example.com'
+  }
+
+  return request(url)
+          .post(path)
+          .set('Accept', 'application/json')
+          .send(body)
+          .expect(specialStatus)
+}
+
+function getMyUserInformation (url: string, accessToken: string) {
+  const path = '/api/v1/users/me'
+
+  return request(url)
+          .get(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+function getUserInformation (url: string, accessToken: string, userId: number) {
+  const path = '/api/v1/users/' + userId
+
+  return request(url)
+    .get(path)
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + accessToken)
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function getUserVideoRating (url: string, accessToken: string, videoId: number) {
+  const path = '/api/v1/users/me/videos/' + videoId + '/rating'
+
+  return request(url)
+          .get(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+function getUsersList (url: string, accessToken: string) {
+  const path = '/api/v1/users'
+
+  return request(url)
+          .get(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string) {
+  const path = '/api/v1/users'
+
+  return request(url)
+          .get(path)
+          .query({ start })
+          .query({ count })
+          .query({ sort })
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+function removeUser (url: string, userId: number, accessToken: string, expectedStatus = 204) {
+  const path = '/api/v1/users'
+
+  return request(url)
+          .delete(path + '/' + userId)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(expectedStatus)
+}
+
+function updateMyUser (url: string, accessToken: string, newPassword: string, displayNSFW?: boolean,
+  email?: string, autoPlayVideo?: boolean) {
+  const path = '/api/v1/users/me'
+
+  const toSend = {}
+  if (newPassword !== undefined && newPassword !== null) toSend['password'] = newPassword
+  if (displayNSFW !== undefined && displayNSFW !== null) toSend['displayNSFW'] = displayNSFW
+  if (autoPlayVideo !== undefined && autoPlayVideo !== null) toSend['autoPlayVideo'] = autoPlayVideo
+  if (email !== undefined && email !== null) toSend['email'] = email
+
+  return request(url)
+    .put(path)
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + accessToken)
+    .send(toSend)
+    .expect(204)
+}
+
+function updateUser (url: string, userId: number, accessToken: string, email: string, videoQuota: number, role: UserRole) {
+  const path = '/api/v1/users/' + userId
+
+  const toSend = {}
+  if (email !== undefined && email !== null) toSend['email'] = email
+  if (videoQuota !== undefined && videoQuota !== null) toSend['videoQuota'] = videoQuota
+  if (role !== undefined && role !== null) toSend['role'] = role
+
+  return request(url)
+          .put(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .send(toSend)
+          .expect(204)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  createUser,
+  registerUser,
+  getMyUserInformation,
+  getUserVideoRating,
+  getUsersList,
+  getUsersListPaginationAndSort,
+  removeUser,
+  updateUser,
+  updateMyUser,
+  getUserInformation
+}
diff --git a/server/tests/utils/video-abuses.ts b/server/tests/utils/video-abuses.ts
deleted file mode 100644 (file)
index f008092..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-import * as request from 'supertest'
-
-function reportVideoAbuse (url: string, token: string, videoId: number, reason: string, specialStatus = 204) {
-  const path = '/api/v1/videos/' + videoId + '/abuse'
-
-  return request(url)
-          .post(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .send({ reason })
-          .expect(specialStatus)
-}
-
-function getVideoAbusesList (url: string, token: string) {
-  const path = '/api/v1/videos/abuse'
-
-  return request(url)
-          .get(path)
-          .query({ sort: 'createdAt' })
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  reportVideoAbuse,
-  getVideoAbusesList
-}
diff --git a/server/tests/utils/video-blacklist.ts b/server/tests/utils/video-blacklist.ts
deleted file mode 100644 (file)
index 3a499f4..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-import * as request from 'supertest'
-
-function addVideoToBlacklist (url: string, token: string, videoId: number, specialStatus = 204) {
-  const path = '/api/v1/videos/' + videoId + '/blacklist'
-
-  return request(url)
-          .post(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .expect(specialStatus)
-}
-
-function removeVideoFromBlacklist (url: string, token: string, videoId: number, specialStatus = 204) {
-  const path = '/api/v1/videos/' + videoId + '/blacklist'
-
-  return request(url)
-          .delete(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .expect(specialStatus)
-}
-
-function getBlacklistedVideosList (url: string, token: string, specialStatus = 200) {
-  const path = '/api/v1/videos/blacklist/'
-
-  return request(url)
-          .get(path)
-          .query({ sort: 'createdAt' })
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .expect(specialStatus)
-          .expect('Content-Type', /json/)
-}
-
-function getSortedBlacklistedVideosList (url: string, token: string, sort: string, specialStatus = 200) {
-  const path = '/api/v1/videos/blacklist/'
-
-  return request(url)
-          .get(path)
-          .query({ sort: sort })
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .expect(specialStatus)
-          .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  addVideoToBlacklist,
-  removeVideoFromBlacklist,
-  getBlacklistedVideosList,
-  getSortedBlacklistedVideosList
-}
diff --git a/server/tests/utils/video-channels.ts b/server/tests/utils/video-channels.ts
deleted file mode 100644 (file)
index 0fb80d3..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-import * as request from 'supertest'
-
-type VideoChannelAttributes = {
-  name?: string
-  description?: string
-}
-
-function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
-  const path = '/api/v1/videos/channels'
-
-  const req = request(url)
-    .get(path)
-    .query({ start: start })
-    .query({ count: count })
-
-  if (sort) req.query({ sort })
-
-  return req.set('Accept', 'application/json')
-            .expect(200)
-            .expect('Content-Type', /json/)
-}
-
-function getAccountVideoChannelsList (url: string, accountId: number | string) {
-  const path = '/api/v1/videos/accounts/' + accountId + '/channels'
-
-  return request(url)
-    .get(path)
-    .set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 204) {
-  const path = '/api/v1/videos/channels'
-
-  // Default attributes
-  let attributes = {
-    name: 'my super video channel',
-    description: 'my super channel description'
-  }
-  attributes = Object.assign(attributes, videoChannelAttributesArg)
-
-  return request(url)
-    .post(path)
-    .send(attributes)
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + token)
-    .expect(expectedStatus)
-}
-
-function updateVideoChannel (url: string, token: string, channelId: number, attributes: VideoChannelAttributes, expectedStatus = 204) {
-  const body = {}
-  const path = '/api/v1/videos/channels/' + channelId
-
-  if (attributes.name) body['name'] = attributes.name
-  if (attributes.description) body['description'] = attributes.description
-
-  return request(url)
-    .put(path)
-    .send(body)
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + token)
-    .expect(expectedStatus)
-}
-
-function deleteVideoChannel (url: string, token: string, channelId: number, expectedStatus = 204) {
-  const path = '/api/v1/videos/channels/'
-
-  return request(url)
-    .delete(path + channelId)
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + token)
-    .expect(expectedStatus)
-}
-
-function getVideoChannel (url: string, channelId: number) {
-  const path = '/api/v1/videos/channels/' + channelId
-
-  return request(url)
-    .get(path)
-    .set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getVideoChannelsList,
-  getAccountVideoChannelsList,
-  addVideoChannel,
-  updateVideoChannel,
-  deleteVideoChannel,
-  getVideoChannel
-}
diff --git a/server/tests/utils/video-comments.ts b/server/tests/utils/video-comments.ts
deleted file mode 100644 (file)
index 8781470..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-import * as request from 'supertest'
-
-function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string) {
-  const path = '/api/v1/videos/' + videoId + '/comment-threads'
-
-  const req = request(url)
-    .get(path)
-    .query({ start: start })
-    .query({ count: count })
-
-  if (sort) req.query({ sort })
-
-  return req.set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function getVideoThreadComments (url: string, videoId: number | string, threadId: number) {
-  const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
-
-  return request(url)
-    .get(path)
-    .set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function addVideoCommentThread (url: string, token: string, videoId: number | string, text: string, expectedStatus = 200) {
-  const path = '/api/v1/videos/' + videoId + '/comment-threads'
-
-  return request(url)
-    .post(path)
-    .send({ text })
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + token)
-    .expect(expectedStatus)
-}
-
-function addVideoCommentReply (
-  url: string,
-  token: string,
-  videoId: number | string,
-  inReplyToCommentId: number,
-  text: string,
-  expectedStatus = 200
-) {
-  const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
-
-  return request(url)
-    .post(path)
-    .send({ text })
-    .set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + token)
-    .expect(expectedStatus)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getVideoCommentThreads,
-  getVideoThreadComments,
-  addVideoCommentThread,
-  addVideoCommentReply
-}
diff --git a/server/tests/utils/videos.ts b/server/tests/utils/videos.ts
deleted file mode 100644 (file)
index fb758cf..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-import { readFile } from 'fs'
-import * as request from 'supertest'
-import { join, isAbsolute } from 'path'
-import * as parseTorrent from 'parse-torrent'
-
-import { makeGetRequest } from './requests'
-import { readFilePromise } from './miscs'
-import { ServerInfo } from './servers'
-import { getMyUserInformation } from './users'
-import { VideoPrivacy } from '../../../shared'
-
-type VideoAttributes = {
-  name?: string
-  category?: number
-  licence?: number
-  language?: number
-  nsfw?: boolean
-  description?: string
-  tags?: string[]
-  channelId?: number
-  privacy?: VideoPrivacy
-  fixture?: string
-}
-
-function getVideoCategories (url: string) {
-  const path = '/api/v1/videos/categories'
-
-  return makeGetRequest(url, path)
-}
-
-function getVideoLicences (url: string) {
-  const path = '/api/v1/videos/licences'
-
-  return makeGetRequest(url, path)
-}
-
-function getVideoLanguages (url: string) {
-  const path = '/api/v1/videos/languages'
-
-  return makeGetRequest(url, path)
-}
-
-function getVideoPrivacies (url: string) {
-  const path = '/api/v1/videos/privacies'
-
-  return makeGetRequest(url, path)
-}
-
-function getVideo (url: string, id: number | string, expectedStatus = 200) {
-  const path = '/api/v1/videos/' + id
-
-  return request(url)
-          .get(path)
-          .set('Accept', 'application/json')
-          .expect(expectedStatus)
-}
-
-function viewVideo (url: string, id: number | string, expectedStatus = 204) {
-  const path = '/api/v1/videos/' + id + '/views'
-
-  return request(url)
-    .post(path)
-    .set('Accept', 'application/json')
-    .expect(expectedStatus)
-}
-
-function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
-  const path = '/api/v1/videos/' + id
-
-  return request(url)
-    .get(path)
-    .set('Authorization', 'Bearer ' + token)
-    .set('Accept', 'application/json')
-    .expect(expectedStatus)
-}
-
-function getVideoDescription (url: string, descriptionPath: string) {
-  return request(url)
-    .get(descriptionPath)
-    .set('Accept', 'application/json')
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function getVideosList (url: string) {
-  const path = '/api/v1/videos'
-
-  return request(url)
-          .get(path)
-          .query({ sort: 'name' })
-          .set('Accept', 'application/json')
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
-  const path = '/api/v1/users/me/videos'
-
-  const req = request(url)
-    .get(path)
-    .query({ start: start })
-    .query({ count: count })
-
-  if (sort) req.query({ sort })
-
-  return req.set('Accept', 'application/json')
-    .set('Authorization', 'Bearer ' + accessToken)
-    .expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
-  const path = '/api/v1/videos'
-
-  const req = request(url)
-              .get(path)
-              .query({ start: start })
-              .query({ count: count })
-
-  if (sort) req.query({ sort })
-
-  return req.set('Accept', 'application/json')
-           .expect(200)
-           .expect('Content-Type', /json/)
-}
-
-function getVideosListSort (url: string, sort: string) {
-  const path = '/api/v1/videos'
-
-  return request(url)
-          .get(path)
-          .query({ sort: sort })
-          .set('Accept', 'application/json')
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-function removeVideo (url: string, token: string, id: number, expectedStatus = 204) {
-  const path = '/api/v1/videos'
-
-  return request(url)
-          .delete(path + '/' + id)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .expect(expectedStatus)
-}
-
-function searchVideo (url: string, search: string) {
-  const path = '/api/v1/videos'
-  const req = request(url)
-    .get(path + '/search')
-    .query({ search })
-    .set('Accept', 'application/json')
-
-  return req.expect(200)
-    .expect('Content-Type', /json/)
-}
-
-function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
-  const path = '/api/v1/videos'
-
-  const req = request(url)
-                .get(path + '/search')
-                .query({ start })
-                .query({ search })
-                .query({ count })
-
-  if (sort) req.query({ sort })
-
-  return req.set('Accept', 'application/json')
-            .expect(200)
-            .expect('Content-Type', /json/)
-}
-
-function searchVideoWithSort (url: string, search: string, sort: string) {
-  const path = '/api/v1/videos'
-
-  return request(url)
-          .get(path + '/search')
-          .query({ search })
-          .query({ sort })
-          .set('Accept', 'application/json')
-          .expect(200)
-          .expect('Content-Type', /json/)
-}
-
-async function testVideoImage (url: string, imageName: string, imagePath: string) {
-  // Don't test images if the node env is not set
-  // Because we need a special ffmpeg version for this test
-  if (process.env['NODE_TEST_IMAGE']) {
-    const res = await request(url)
-                        .get(imagePath)
-                        .expect(200)
-
-    const data = await readFilePromise(join(__dirname, '..', 'api', 'fixtures', imageName + '.jpg'))
-
-    return data.equals(res.body)
-  } else {
-    console.log('Do not test images. Enable it by setting NODE_TEST_IMAGE env variable.')
-    return true
-  }
-}
-
-async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
-  const path = '/api/v1/videos/upload'
-  let defaultChannelId = '1'
-
-  try {
-    const res = await getMyUserInformation(url, accessToken)
-    defaultChannelId = res.body.videoChannels[0].id
-  } catch (e) { /* empty */ }
-
-  // Default attributes
-  let attributes = {
-    name: 'my super video',
-    category: 5,
-    licence: 4,
-    language: 3,
-    channelId: defaultChannelId,
-    nsfw: true,
-    description: 'my super description',
-    tags: [ 'tag' ],
-    privacy: VideoPrivacy.PUBLIC,
-    fixture: 'video_short.webm'
-  }
-  attributes = Object.assign(attributes, videoAttributesArg)
-
-  const req = request(url)
-              .post(path)
-              .set('Accept', 'application/json')
-              .set('Authorization', 'Bearer ' + accessToken)
-              .field('name', attributes.name)
-              .field('category', attributes.category.toString())
-              .field('licence', attributes.licence.toString())
-              .field('nsfw', JSON.stringify(attributes.nsfw))
-              .field('description', attributes.description)
-              .field('privacy', attributes.privacy.toString())
-              .field('channelId', attributes.channelId)
-
-  if (attributes.language !== undefined) {
-    req.field('language', attributes.language.toString())
-  }
-
-  for (let i = 0; i < attributes.tags.length; i++) {
-    req.field('tags[' + i + ']', attributes.tags[i])
-  }
-
-  let filePath = ''
-  if (isAbsolute(attributes.fixture)) {
-    filePath = attributes.fixture
-  } else {
-    filePath = join(__dirname, '..', 'api', 'fixtures', attributes.fixture)
-  }
-
-  return req.attach('videofile', filePath)
-            .expect(specialStatus)
-}
-
-function updateVideo (url: string, accessToken: string, id: number, attributes: VideoAttributes, specialStatus = 204) {
-  const path = '/api/v1/videos/' + id
-  const body = {}
-
-  if (attributes.name) body['name'] = attributes.name
-  if (attributes.category) body['category'] = attributes.category
-  if (attributes.licence) body['licence'] = attributes.licence
-  if (attributes.language) body['language'] = attributes.language
-  if (attributes.nsfw) body['nsfw'] = attributes.nsfw
-  if (attributes.description) body['description'] = attributes.description
-  if (attributes.tags) body['tags'] = attributes.tags
-  if (attributes.privacy) body['privacy'] = attributes.privacy
-
-  return request(url)
-          .put(path)
-          .send(body)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .expect(specialStatus)
-}
-
-function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
-  const path = '/api/v1/videos/' + id + '/rate'
-
-  return request(url)
-          .put(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + accessToken)
-          .send({ rating })
-          .expect(specialStatus)
-}
-
-function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
-  return new Promise<any>((res, rej) => {
-    const torrentName = videoUUID + '-' + resolution + '.torrent'
-    const torrentPath = join(__dirname, '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
-    readFile(torrentPath, (err, data) => {
-      if (err) return rej(err)
-
-      return res(parseTorrent(data))
-    })
-  })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  getVideoDescription,
-  getVideoCategories,
-  getVideoLicences,
-  getVideoPrivacies,
-  getVideoLanguages,
-  getMyVideos,
-  getVideo,
-  getVideoWithToken,
-  getVideosList,
-  getVideosListPagination,
-  getVideosListSort,
-  removeVideo,
-  searchVideo,
-  searchVideoWithPagination,
-  searchVideoWithSort,
-  testVideoImage,
-  uploadVideo,
-  updateVideo,
-  rateVideo,
-  viewVideo,
-  parseTorrentVideo
-}
diff --git a/server/tests/utils/videos/services.ts b/server/tests/utils/videos/services.ts
new file mode 100644 (file)
index 0000000..1a53dd4
--- /dev/null
@@ -0,0 +1,23 @@
+import * as request from 'supertest'
+
+function getOEmbed (url: string, oembedUrl: string, format?: string, maxHeight?: number, maxWidth?: number) {
+  const path = '/services/oembed'
+  const query = {
+    url: oembedUrl,
+    format,
+    maxheight: maxHeight,
+    maxwidth: maxWidth
+  }
+
+  return request(url)
+          .get(path)
+          .query(query)
+          .set('Accept', 'application/json')
+          .expect(200)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getOEmbed
+}
diff --git a/server/tests/utils/videos/video-abuses.ts b/server/tests/utils/videos/video-abuses.ts
new file mode 100644 (file)
index 0000000..f008092
--- /dev/null
@@ -0,0 +1,31 @@
+import * as request from 'supertest'
+
+function reportVideoAbuse (url: string, token: string, videoId: number, reason: string, specialStatus = 204) {
+  const path = '/api/v1/videos/' + videoId + '/abuse'
+
+  return request(url)
+          .post(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + token)
+          .send({ reason })
+          .expect(specialStatus)
+}
+
+function getVideoAbusesList (url: string, token: string) {
+  const path = '/api/v1/videos/abuse'
+
+  return request(url)
+          .get(path)
+          .query({ sort: 'createdAt' })
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + token)
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  reportVideoAbuse,
+  getVideoAbusesList
+}
diff --git a/server/tests/utils/videos/video-blacklist.ts b/server/tests/utils/videos/video-blacklist.ts
new file mode 100644 (file)
index 0000000..3a499f4
--- /dev/null
@@ -0,0 +1,54 @@
+import * as request from 'supertest'
+
+function addVideoToBlacklist (url: string, token: string, videoId: number, specialStatus = 204) {
+  const path = '/api/v1/videos/' + videoId + '/blacklist'
+
+  return request(url)
+          .post(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + token)
+          .expect(specialStatus)
+}
+
+function removeVideoFromBlacklist (url: string, token: string, videoId: number, specialStatus = 204) {
+  const path = '/api/v1/videos/' + videoId + '/blacklist'
+
+  return request(url)
+          .delete(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + token)
+          .expect(specialStatus)
+}
+
+function getBlacklistedVideosList (url: string, token: string, specialStatus = 200) {
+  const path = '/api/v1/videos/blacklist/'
+
+  return request(url)
+          .get(path)
+          .query({ sort: 'createdAt' })
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + token)
+          .expect(specialStatus)
+          .expect('Content-Type', /json/)
+}
+
+function getSortedBlacklistedVideosList (url: string, token: string, sort: string, specialStatus = 200) {
+  const path = '/api/v1/videos/blacklist/'
+
+  return request(url)
+          .get(path)
+          .query({ sort: sort })
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + token)
+          .expect(specialStatus)
+          .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  addVideoToBlacklist,
+  removeVideoFromBlacklist,
+  getBlacklistedVideosList,
+  getSortedBlacklistedVideosList
+}
diff --git a/server/tests/utils/videos/video-channels.ts b/server/tests/utils/videos/video-channels.ts
new file mode 100644 (file)
index 0000000..0fb80d3
--- /dev/null
@@ -0,0 +1,95 @@
+import * as request from 'supertest'
+
+type VideoChannelAttributes = {
+  name?: string
+  description?: string
+}
+
+function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
+  const path = '/api/v1/videos/channels'
+
+  const req = request(url)
+    .get(path)
+    .query({ start: start })
+    .query({ count: count })
+
+  if (sort) req.query({ sort })
+
+  return req.set('Accept', 'application/json')
+            .expect(200)
+            .expect('Content-Type', /json/)
+}
+
+function getAccountVideoChannelsList (url: string, accountId: number | string) {
+  const path = '/api/v1/videos/accounts/' + accountId + '/channels'
+
+  return request(url)
+    .get(path)
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 204) {
+  const path = '/api/v1/videos/channels'
+
+  // Default attributes
+  let attributes = {
+    name: 'my super video channel',
+    description: 'my super channel description'
+  }
+  attributes = Object.assign(attributes, videoChannelAttributesArg)
+
+  return request(url)
+    .post(path)
+    .send(attributes)
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + token)
+    .expect(expectedStatus)
+}
+
+function updateVideoChannel (url: string, token: string, channelId: number, attributes: VideoChannelAttributes, expectedStatus = 204) {
+  const body = {}
+  const path = '/api/v1/videos/channels/' + channelId
+
+  if (attributes.name) body['name'] = attributes.name
+  if (attributes.description) body['description'] = attributes.description
+
+  return request(url)
+    .put(path)
+    .send(body)
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + token)
+    .expect(expectedStatus)
+}
+
+function deleteVideoChannel (url: string, token: string, channelId: number, expectedStatus = 204) {
+  const path = '/api/v1/videos/channels/'
+
+  return request(url)
+    .delete(path + channelId)
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + token)
+    .expect(expectedStatus)
+}
+
+function getVideoChannel (url: string, channelId: number) {
+  const path = '/api/v1/videos/channels/' + channelId
+
+  return request(url)
+    .get(path)
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getVideoChannelsList,
+  getAccountVideoChannelsList,
+  addVideoChannel,
+  updateVideoChannel,
+  deleteVideoChannel,
+  getVideoChannel
+}
diff --git a/server/tests/utils/videos/video-comments.ts b/server/tests/utils/videos/video-comments.ts
new file mode 100644 (file)
index 0000000..8781470
--- /dev/null
@@ -0,0 +1,64 @@
+import * as request from 'supertest'
+
+function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string) {
+  const path = '/api/v1/videos/' + videoId + '/comment-threads'
+
+  const req = request(url)
+    .get(path)
+    .query({ start: start })
+    .query({ count: count })
+
+  if (sort) req.query({ sort })
+
+  return req.set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function getVideoThreadComments (url: string, videoId: number | string, threadId: number) {
+  const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
+
+  return request(url)
+    .get(path)
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function addVideoCommentThread (url: string, token: string, videoId: number | string, text: string, expectedStatus = 200) {
+  const path = '/api/v1/videos/' + videoId + '/comment-threads'
+
+  return request(url)
+    .post(path)
+    .send({ text })
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + token)
+    .expect(expectedStatus)
+}
+
+function addVideoCommentReply (
+  url: string,
+  token: string,
+  videoId: number | string,
+  inReplyToCommentId: number,
+  text: string,
+  expectedStatus = 200
+) {
+  const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
+
+  return request(url)
+    .post(path)
+    .send({ text })
+    .set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + token)
+    .expect(expectedStatus)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getVideoCommentThreads,
+  getVideoThreadComments,
+  addVideoCommentThread,
+  addVideoCommentReply
+}
diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts
new file mode 100644 (file)
index 0000000..6de1b8c
--- /dev/null
@@ -0,0 +1,323 @@
+import { readFile } from 'fs'
+import * as parseTorrent from 'parse-torrent'
+import { isAbsolute, join } from 'path'
+import * as request from 'supertest'
+import { getMyUserInformation, makeGetRequest, readFilePromise, ServerInfo } from '../'
+import { VideoPrivacy } from '../../../../shared/models/videos'
+
+type VideoAttributes = {
+  name?: string
+  category?: number
+  licence?: number
+  language?: number
+  nsfw?: boolean
+  description?: string
+  tags?: string[]
+  channelId?: number
+  privacy?: VideoPrivacy
+  fixture?: string
+}
+
+function getVideoCategories (url: string) {
+  const path = '/api/v1/videos/categories'
+
+  return makeGetRequest(url, path)
+}
+
+function getVideoLicences (url: string) {
+  const path = '/api/v1/videos/licences'
+
+  return makeGetRequest(url, path)
+}
+
+function getVideoLanguages (url: string) {
+  const path = '/api/v1/videos/languages'
+
+  return makeGetRequest(url, path)
+}
+
+function getVideoPrivacies (url: string) {
+  const path = '/api/v1/videos/privacies'
+
+  return makeGetRequest(url, path)
+}
+
+function getVideo (url: string, id: number | string, expectedStatus = 200) {
+  const path = '/api/v1/videos/' + id
+
+  return request(url)
+          .get(path)
+          .set('Accept', 'application/json')
+          .expect(expectedStatus)
+}
+
+function viewVideo (url: string, id: number | string, expectedStatus = 204) {
+  const path = '/api/v1/videos/' + id + '/views'
+
+  return request(url)
+    .post(path)
+    .set('Accept', 'application/json')
+    .expect(expectedStatus)
+}
+
+function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
+  const path = '/api/v1/videos/' + id
+
+  return request(url)
+    .get(path)
+    .set('Authorization', 'Bearer ' + token)
+    .set('Accept', 'application/json')
+    .expect(expectedStatus)
+}
+
+function getVideoDescription (url: string, descriptionPath: string) {
+  return request(url)
+    .get(descriptionPath)
+    .set('Accept', 'application/json')
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function getVideosList (url: string) {
+  const path = '/api/v1/videos'
+
+  return request(url)
+          .get(path)
+          .query({ sort: 'name' })
+          .set('Accept', 'application/json')
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
+  const path = '/api/v1/users/me/videos'
+
+  const req = request(url)
+    .get(path)
+    .query({ start: start })
+    .query({ count: count })
+
+  if (sort) req.query({ sort })
+
+  return req.set('Accept', 'application/json')
+    .set('Authorization', 'Bearer ' + accessToken)
+    .expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
+  const path = '/api/v1/videos'
+
+  const req = request(url)
+              .get(path)
+              .query({ start: start })
+              .query({ count: count })
+
+  if (sort) req.query({ sort })
+
+  return req.set('Accept', 'application/json')
+           .expect(200)
+           .expect('Content-Type', /json/)
+}
+
+function getVideosListSort (url: string, sort: string) {
+  const path = '/api/v1/videos'
+
+  return request(url)
+          .get(path)
+          .query({ sort: sort })
+          .set('Accept', 'application/json')
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+function removeVideo (url: string, token: string, id: number, expectedStatus = 204) {
+  const path = '/api/v1/videos'
+
+  return request(url)
+          .delete(path + '/' + id)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + token)
+          .expect(expectedStatus)
+}
+
+function searchVideo (url: string, search: string) {
+  const path = '/api/v1/videos'
+  const req = request(url)
+    .get(path + '/search')
+    .query({ search })
+    .set('Accept', 'application/json')
+
+  return req.expect(200)
+    .expect('Content-Type', /json/)
+}
+
+function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
+  const path = '/api/v1/videos'
+
+  const req = request(url)
+                .get(path + '/search')
+                .query({ start })
+                .query({ search })
+                .query({ count })
+
+  if (sort) req.query({ sort })
+
+  return req.set('Accept', 'application/json')
+            .expect(200)
+            .expect('Content-Type', /json/)
+}
+
+function searchVideoWithSort (url: string, search: string, sort: string) {
+  const path = '/api/v1/videos'
+
+  return request(url)
+          .get(path + '/search')
+          .query({ search })
+          .query({ sort })
+          .set('Accept', 'application/json')
+          .expect(200)
+          .expect('Content-Type', /json/)
+}
+
+async function testVideoImage (url: string, imageName: string, imagePath: string) {
+  // Don't test images if the node env is not set
+  // Because we need a special ffmpeg version for this test
+  if (process.env['NODE_TEST_IMAGE']) {
+    const res = await request(url)
+                        .get(imagePath)
+                        .expect(200)
+
+    const data = await readFilePromise(join(__dirname, '..', 'api', 'fixtures', imageName + '.jpg'))
+
+    return data.equals(res.body)
+  } else {
+    console.log('Do not test images. Enable it by setting NODE_TEST_IMAGE env variable.')
+    return true
+  }
+}
+
+async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
+  const path = '/api/v1/videos/upload'
+  let defaultChannelId = '1'
+
+  try {
+    const res = await getMyUserInformation(url, accessToken)
+    defaultChannelId = res.body.videoChannels[0].id
+  } catch (e) { /* empty */ }
+
+  // Default attributes
+  let attributes = {
+    name: 'my super video',
+    category: 5,
+    licence: 4,
+    language: 3,
+    channelId: defaultChannelId,
+    nsfw: true,
+    description: 'my super description',
+    tags: [ 'tag' ],
+    privacy: VideoPrivacy.PUBLIC,
+    fixture: 'video_short.webm'
+  }
+  attributes = Object.assign(attributes, videoAttributesArg)
+
+  const req = request(url)
+              .post(path)
+              .set('Accept', 'application/json')
+              .set('Authorization', 'Bearer ' + accessToken)
+              .field('name', attributes.name)
+              .field('category', attributes.category.toString())
+              .field('licence', attributes.licence.toString())
+              .field('nsfw', JSON.stringify(attributes.nsfw))
+              .field('description', attributes.description)
+              .field('privacy', attributes.privacy.toString())
+              .field('channelId', attributes.channelId)
+
+  if (attributes.language !== undefined) {
+    req.field('language', attributes.language.toString())
+  }
+
+  for (let i = 0; i < attributes.tags.length; i++) {
+    req.field('tags[' + i + ']', attributes.tags[i])
+  }
+
+  let filePath = ''
+  if (isAbsolute(attributes.fixture)) {
+    filePath = attributes.fixture
+  } else {
+    filePath = join(__dirname, '..', 'api', 'fixtures', attributes.fixture)
+  }
+
+  return req.attach('videofile', filePath)
+            .expect(specialStatus)
+}
+
+function updateVideo (url: string, accessToken: string, id: number, attributes: VideoAttributes, specialStatus = 204) {
+  const path = '/api/v1/videos/' + id
+  const body = {}
+
+  if (attributes.name) body['name'] = attributes.name
+  if (attributes.category) body['category'] = attributes.category
+  if (attributes.licence) body['licence'] = attributes.licence
+  if (attributes.language) body['language'] = attributes.language
+  if (attributes.nsfw) body['nsfw'] = attributes.nsfw
+  if (attributes.description) body['description'] = attributes.description
+  if (attributes.tags) body['tags'] = attributes.tags
+  if (attributes.privacy) body['privacy'] = attributes.privacy
+
+  return request(url)
+          .put(path)
+          .send(body)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .expect(specialStatus)
+}
+
+function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
+  const path = '/api/v1/videos/' + id + '/rate'
+
+  return request(url)
+          .put(path)
+          .set('Accept', 'application/json')
+          .set('Authorization', 'Bearer ' + accessToken)
+          .send({ rating })
+          .expect(specialStatus)
+}
+
+function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
+  return new Promise<any>((res, rej) => {
+    const torrentName = videoUUID + '-' + resolution + '.torrent'
+    const torrentPath = join(__dirname, '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
+    readFile(torrentPath, (err, data) => {
+      if (err) return rej(err)
+
+      return res(parseTorrent(data))
+    })
+  })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getVideoDescription,
+  getVideoCategories,
+  getVideoLicences,
+  getVideoPrivacies,
+  getVideoLanguages,
+  getMyVideos,
+  getVideo,
+  getVideoWithToken,
+  getVideosList,
+  getVideosListPagination,
+  getVideosListSort,
+  removeVideo,
+  searchVideo,
+  searchVideoWithPagination,
+  searchVideoWithSort,
+  testVideoImage,
+  uploadVideo,
+  updateVideo,
+  rateVideo,
+  viewVideo,
+  parseTorrentVideo
+}