Optimize signature verification

This commit is contained in:
Chocobozzz 2017-11-17 15:20:42 +01:00
parent 975e6e0e44
commit 9a27cdc27c
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
29 changed files with 444 additions and 584 deletions

View File

@ -42,7 +42,7 @@ export class FollowService {
hosts: notEmptyHosts hosts: notEmptyHosts
} }
return this.authHttp.post(FollowService.BASE_APPLICATION_URL + '/follow', body) return this.authHttp.post(FollowService.BASE_APPLICATION_URL + '/following', body)
.map(this.restExtractor.extractDataBool) .map(this.restExtractor.extractDataBool)
.catch(res => this.restExtractor.handleError(res)) .catch(res => this.restExtractor.handleError(res))
} }

View File

@ -25,7 +25,7 @@ serverFollowsRouter.get('/following',
asyncMiddleware(listFollowing) asyncMiddleware(listFollowing)
) )
serverFollowsRouter.post('/follow', serverFollowsRouter.post('/following',
authenticate, authenticate,
ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
followValidator, followValidator,

View File

@ -8,7 +8,7 @@ import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-ac
import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object' import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object'
import { ResultList } from '../../shared/models/result-list.model' import { ResultList } from '../../shared/models/result-list.model'
import { database as db, REMOTE_SCHEME } from '../initializers' import { database as db, REMOTE_SCHEME } from '../initializers'
import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants' import { ACTIVITY_PUB, CONFIG, STATIC_PATHS } from '../initializers/constants'
import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc' import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc'
import { sendVideoAnnounce } from '../lib/activitypub/send-request' import { sendVideoAnnounce } from '../lib/activitypub/send-request'
import { sendVideoChannelAnnounce } from '../lib/index' import { sendVideoChannelAnnounce } from '../lib/index'
@ -99,7 +99,7 @@ async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
uri: accountUrl, uri: accountUrl,
method: 'GET', method: 'GET',
headers: { headers: {
'Accept': ACTIVITY_PUB_ACCEPT_HEADER 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
} }
} }
@ -157,7 +157,7 @@ async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChan
uri: videoChannelUrl, uri: videoChannelUrl,
method: 'GET', method: 'GET',
headers: { headers: {
'Accept': ACTIVITY_PUB_ACCEPT_HEADER 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
} }
} }

View File

@ -0,0 +1,20 @@
import * as AsyncLRU from 'async-lru'
import * as jsonld from 'jsonld'
import * as jsig from 'jsonld-signatures'
jsig.use('jsonld', jsonld)
const nodeDocumentLoader = jsonld.documentLoaders.node()
const lru = new AsyncLRU({
max: 10,
load: (key, cb) => {
nodeDocumentLoader(key, cb)
}
})
jsonld.documentLoader = (url, cb) => {
lru.get(url, cb)
}
export { jsig }

View File

@ -28,6 +28,10 @@ function isBaseActivityValid (activity: any, type: string) {
( (
activity.to === undefined || activity.to === undefined ||
(Array.isArray(activity.to) && activity.to.every(t => isActivityPubUrlValid(t))) (Array.isArray(activity.to) && activity.to.every(t => isActivityPubUrlValid(t)))
) &&
(
activity.cc === undefined ||
(Array.isArray(activity.cc) && activity.cc.every(t => isActivityPubUrlValid(t)))
) )
} }

View File

@ -1,7 +1,3 @@
import * as jsonld from 'jsonld'
import * as jsig from 'jsonld-signatures'
jsig.use('jsonld', jsonld)
import { import {
PRIVATE_RSA_KEY_SIZE, PRIVATE_RSA_KEY_SIZE,
BCRYPT_SALT_SIZE BCRYPT_SALT_SIZE
@ -15,6 +11,7 @@ import {
} from './core-utils' } from './core-utils'
import { logger } from './logger' import { logger } from './logger'
import { AccountInstance } from '../models/account/account-interface' import { AccountInstance } from '../models/account/account-interface'
import { jsig } from './custom-jsonld-signature'
async function createPrivateAndPublicKeys () { async function createPrivateAndPublicKeys () {
logger.info('Generating a RSA key...') logger.info('Generating a RSA key...')

View File

@ -223,9 +223,10 @@ const FRIEND_SCORE = {
} }
const SERVER_ACCOUNT_NAME = 'peertube' const SERVER_ACCOUNT_NAME = 'peertube'
const ACTIVITY_PUB_ACCEPT_HEADER = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
const ACTIVITY_PUB = { const ACTIVITY_PUB = {
ACCEPT_HEADER: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
PUBLIC: 'https://www.w3.org/ns/activitystreams#Public',
COLLECTION_ITEMS_PER_PAGE: 10, COLLECTION_ITEMS_PER_PAGE: 10,
URL_MIME_TYPES: { URL_MIME_TYPES: {
VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT
@ -349,7 +350,6 @@ export {
SERVERS_SCORE, SERVERS_SCORE,
PREVIEWS_SIZE, PREVIEWS_SIZE,
REMOTE_SCHEME, REMOTE_SCHEME,
ACTIVITY_PUB_ACCEPT_HEADER,
FOLLOW_STATES, FOLLOW_STATES,
SEARCHABLE_COLUMNS, SEARCHABLE_COLUMNS,
SERVER_ACCOUNT_NAME, SERVER_ACCOUNT_NAME,

View File

@ -2,11 +2,12 @@ import * as magnetUtil from 'magnet-uri'
import { VideoTorrentObject } from '../../../shared' import { VideoTorrentObject } from '../../../shared'
import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
import { VIDEO_MIMETYPE_EXT } from '../../initializers/constants' import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../initializers/constants'
import { AccountInstance } from '../../models/account/account-interface' import { AccountInstance } from '../../models/account/account-interface'
import { VideoChannelInstance } from '../../models/video/video-channel-interface' import { VideoChannelInstance } from '../../models/video/video-channel-interface'
import { VideoFileAttributes } from '../../models/video/video-file-interface' import { VideoFileAttributes } from '../../models/video/video-file-interface'
import { VideoAttributes, VideoInstance } from '../../models/video/video-interface' import { VideoAttributes, VideoInstance } from '../../models/video/video-interface'
import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) { function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
return { return {
@ -23,8 +24,14 @@ function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChan
async function videoActivityObjectToDBAttributes ( async function videoActivityObjectToDBAttributes (
videoChannel: VideoChannelInstance, videoChannel: VideoChannelInstance,
videoObject: VideoTorrentObject videoObject: VideoTorrentObject,
to: string[] = [],
cc: string[] = []
) { ) {
let privacy = VideoPrivacy.PRIVATE
if (to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.PUBLIC
else if (cc.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.UNLISTED
const duration = videoObject.duration.replace(/[^\d]+/, '') const duration = videoObject.duration.replace(/[^\d]+/, '')
const videoData: VideoAttributes = { const videoData: VideoAttributes = {
name: videoObject.name, name: videoObject.name,
@ -43,11 +50,8 @@ async function videoActivityObjectToDBAttributes (
views: videoObject.views, views: videoObject.views,
likes: 0, likes: 0,
dislikes: 0, dislikes: 0,
// likes: videoToCreateData.likes,
// dislikes: videoToCreateData.dislikes,
remote: true, remote: true,
privacy: 1 privacy
// privacy: videoToCreateData.privacy
} }
return videoData return videoData

View File

@ -17,7 +17,7 @@ async function processAddActivity (activity: ActivityAdd) {
const videoChannelUrl = activity.target const videoChannelUrl = activity.target
const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl) const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl)
return processAddVideo(account, videoChannel, activityObject as VideoTorrentObject) return processAddVideo(account, activity, videoChannel, activityObject as VideoTorrentObject)
} }
logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
@ -32,16 +32,21 @@ export {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function processAddVideo (account: AccountInstance, videoChannel: VideoChannelInstance, video: VideoTorrentObject) { function processAddVideo (account: AccountInstance, activity: ActivityAdd, videoChannel: VideoChannelInstance, video: VideoTorrentObject) {
const options = { const options = {
arguments: [ account, videoChannel, video ], arguments: [ account, activity, videoChannel, video ],
errorMessage: 'Cannot insert the remote video with many retries.' errorMessage: 'Cannot insert the remote video with many retries.'
} }
return retryTransactionWrapper(addRemoteVideo, options) return retryTransactionWrapper(addRemoteVideo, options)
} }
function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelInstance, videoToCreateData: VideoTorrentObject) { function addRemoteVideo (
account: AccountInstance,
activity: ActivityAdd,
videoChannel: VideoChannelInstance,
videoToCreateData: VideoTorrentObject
) {
logger.debug('Adding remote video %s.', videoToCreateData.url) logger.debug('Adding remote video %s.', videoToCreateData.url)
return db.sequelize.transaction(async t => { return db.sequelize.transaction(async t => {
@ -54,7 +59,7 @@ function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelIns
const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t) const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.') if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.')
const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData) const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc)
const video = db.Video.build(videoData) const video = db.Video.build(videoData)
// Don't block on request // Don't block on request

View File

@ -70,7 +70,6 @@ async function updateRemoteVideo (account: AccountInstance, videoAttributesToUpd
videoInstance.set('views', videoData.views) videoInstance.set('views', videoData.views)
// videoInstance.set('likes', videoData.likes) // videoInstance.set('likes', videoData.likes)
// videoInstance.set('dislikes', videoData.dislikes) // videoInstance.set('dislikes', videoData.dislikes)
// videoInstance.set('privacy', videoData.privacy)
await videoInstance.save(sequelizeOptions) await videoInstance.save(sequelizeOptions)

View File

@ -13,6 +13,8 @@ import { database as db } from '../../initializers'
import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../models' import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../models'
import { VideoAbuseInstance } from '../../models/video/video-abuse-interface' import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
import { activitypubHttpJobScheduler } from '../jobs' import { activitypubHttpJobScheduler } from '../jobs'
import { ACTIVITY_PUB } from '../../initializers/constants'
import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
const byAccount = videoChannel.Account const byAccount = videoChannel.Account
@ -50,7 +52,7 @@ async function sendAddVideo (video: VideoInstance, t: Transaction) {
const byAccount = video.VideoChannel.Account const byAccount = video.VideoChannel.Account
const videoObject = video.toActivityPubObject() const videoObject = video.toActivityPubObject()
const data = await addActivityData(video.url, byAccount, video.VideoChannel.url, videoObject) const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject)
return broadcastToFollowers(data, byAccount, [ byAccount ], t) return broadcastToFollowers(data, byAccount, [ byAccount ], t)
} }
@ -96,7 +98,7 @@ async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstan
const url = getActivityPubUrl('video', video.uuid) + '#announce' const url = getActivityPubUrl('video', video.uuid) + '#announce'
const videoChannel = video.VideoChannel const videoChannel = video.VideoChannel
const announcedActivity = await addActivityData(url, videoChannel.Account, videoChannel.url, video.toActivityPubObject()) const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
const data = await announceActivityData(url, byAccount, announcedActivity) const data = await announceActivityData(url, byAccount, announcedActivity)
return broadcastToFollowers(data, byAccount, [ byAccount ], t) return broadcastToFollowers(data, byAccount, [ byAccount ], t)
@ -167,19 +169,32 @@ async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: s
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload) return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
} }
async function getPublicActivityTo (account: AccountInstance) { async function getAudience (accountSender: AccountInstance, isPublic = true) {
const inboxUrls = await account.getFollowerSharedInboxUrls() const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public') // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
let to = []
let cc = []
if (isPublic) {
to = [ ACTIVITY_PUB.PUBLIC ]
cc = followerInboxUrls
} else { // Unlisted
to = followerInboxUrls
cc = [ ACTIVITY_PUB.PUBLIC ]
}
return { to, cc }
} }
async function createActivityData (url: string, byAccount: AccountInstance, object: any) { async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
const to = await getPublicActivityTo(byAccount) const { to, cc } = await getAudience(byAccount)
const activity: ActivityCreate = { const activity: ActivityCreate = {
type: 'Create', type: 'Create',
id: url, id: url,
actor: byAccount.url, actor: byAccount.url,
to, to,
cc,
object object
} }
@ -187,12 +202,13 @@ async function createActivityData (url: string, byAccount: AccountInstance, obje
} }
async function updateActivityData (url: string, byAccount: AccountInstance, object: any) { async function updateActivityData (url: string, byAccount: AccountInstance, object: any) {
const to = await getPublicActivityTo(byAccount) const { to, cc } = await getAudience(byAccount)
const activity: ActivityUpdate = { const activity: ActivityUpdate = {
type: 'Update', type: 'Update',
id: url, id: url,
actor: byAccount.url, actor: byAccount.url,
to, to,
cc,
object object
} }
@ -209,13 +225,16 @@ async function deleteActivityData (url: string, byAccount: AccountInstance) {
return activity return activity
} }
async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any) { async function addActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, target: string, object: any) {
const to = await getPublicActivityTo(byAccount) const videoPublic = video.privacy === VideoPrivacy.PUBLIC
const { to, cc } = await getAudience(byAccount, videoPublic)
const activity: ActivityAdd = { const activity: ActivityAdd = {
type: 'Add', type: 'Add',
id: url, id: url,
actor: byAccount.url, actor: byAccount.url,
to, to,
cc,
object, object,
target target
} }

View File

@ -1,9 +1,10 @@
import { NextFunction, Request, Response, RequestHandler } from 'express' import { eachSeries } from 'async'
import { NextFunction, Request, RequestHandler, Response } from 'express'
import { ActivityPubSignature } from '../../shared' import { ActivityPubSignature } from '../../shared'
import { isSignatureVerified, logger } from '../helpers' import { isSignatureVerified, logger } from '../helpers'
import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub' import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub'
import { database as db, ACTIVITY_PUB_ACCEPT_HEADER } from '../initializers' import { database as db } from '../initializers'
import { each, eachSeries, waterfall } from 'async' import { ACTIVITY_PUB } from '../initializers/constants'
async function checkSignature (req: Request, res: Response, next: NextFunction) { async function checkSignature (req: Request, res: Response, next: NextFunction) {
const signatureObject: ActivityPubSignature = req.body.signature const signatureObject: ActivityPubSignature = req.body.signature
@ -37,7 +38,7 @@ async function checkSignature (req: Request, res: Response, next: NextFunction)
function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) { function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) {
return (req: Request, res: Response, next: NextFunction) => { return (req: Request, res: Response, next: NextFunction) => {
if (req.header('Accept') !== ACTIVITY_PUB_ACCEPT_HEADER) { if (req.header('Accept') !== ACTIVITY_PUB.ACCEPT_HEADER) {
return next() return next()
} }

View File

@ -550,6 +550,7 @@ toFormattedDetailsJSON = function (this: VideoInstance) {
toActivityPubObject = function (this: VideoInstance) { toActivityPubObject = function (this: VideoInstance) {
const { baseUrlHttp, baseUrlWs } = getBaseUrls(this) const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
if (!this.Tags) this.Tags = []
const tag = this.Tags.map(t => ({ const tag = this.Tags.map(t => ({
type: 'Hashtag' as 'Hashtag', type: 'Hashtag' as 'Hashtag',

View File

@ -0,0 +1,222 @@
/* tslint:disable:no-unused-expression */
import * as request from 'supertest'
import 'mocha'
import {
ServerInfo,
flushTests,
runServer,
createUser,
loginAndGetAccessToken,
setAccessTokensToServers,
killallServers,
makePostBodyRequest
} from '../../utils'
describe('Test server follows API validators', function () {
let server: ServerInfo
// ---------------------------------------------------------------
before(async function () {
this.timeout(45000)
await flushTests()
server = await runServer(1)
await setAccessTokensToServers([ server ])
})
describe('When managing following', function () {
let userAccessToken = null
before(async function () {
await createUser(server.url, server.accessToken, 'user1', 'password')
server.user = {
username: 'user1',
password: 'password'
}
userAccessToken = await loginAndGetAccessToken(server)
})
describe('When adding follows', function () {
const path = '/api/v1/server/following'
const body = {
hosts: [ 'localhost:9002' ]
}
it('Should fail without hosts', async function () {
await request(server.url)
.post(path)
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if hosts is not an array', async function () {
await request(server.url)
.post(path)
.send({ hosts: 'localhost:9002' })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if the array is not composed by hosts', async function () {
await request(server.url)
.post(path)
.send({ hosts: [ 'localhost:9002', 'localhost:coucou' ] })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if the array is composed with http schemes', async function () {
await request(server.url)
.post(path)
.send({ hosts: [ 'localhost:9002', 'http://localhost:9003' ] })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if hosts are not unique', async function () {
await request(server.url)
.post(path)
.send({ urls: [ 'localhost:9002', 'localhost:9002' ] })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with an invalid token', async function () {
await request(server.url)
.post(path)
.send(body)
.set('Authorization', 'Bearer fake_token')
.set('Accept', 'application/json')
.expect(401)
})
it('Should fail if the user is not an administrator', async function () {
await request(server.url)
.post(path)
.send(body)
.set('Authorization', 'Bearer ' + userAccessToken)
.set('Accept', 'application/json')
.expect(403)
})
})
describe('When listing followings', function () {
const path = '/api/v1/server/following'
it('Should fail with a bad start pagination', async function () {
await request(server.url)
.get(path)
.query({ start: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with a bad count pagination', async function () {
await request(server.url)
.get(path)
.query({ count: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with an incorrect sort', async function () {
await request(server.url)
.get(path)
.query({ sort: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
})
describe('When listing followers', function () {
const path = '/api/v1/server/followers'
it('Should fail with a bad start pagination', async function () {
await request(server.url)
.get(path)
.query({ start: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with a bad count pagination', async function () {
await request(server.url)
.get(path)
.query({ count: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with an incorrect sort', async function () {
await request(server.url)
.get(path)
.query({ sort: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
})
describe('When removing following', function () {
// it('Should fail with an invalid token', async function () {
// await request(server.url)
// .delete(path + '/1')
// .set('Authorization', 'Bearer faketoken')
// .set('Accept', 'application/json')
// .expect(401)
// })
//
// it('Should fail if the user is not an administrator', async function () {
// await request(server.url)
// .delete(path + '/1')
// .set('Authorization', 'Bearer ' + userAccessToken)
// .set('Accept', 'application/json')
// .expect(403)
// })
//
// it('Should fail with an undefined id', async function () {
// await request(server.url)
// .delete(path + '/' + undefined)
// .set('Authorization', 'Bearer ' + server.accessToken)
// .set('Accept', 'application/json')
// .expect(400)
// })
//
// it('Should fail with an invalid id', async function () {
// await request(server.url)
// .delete(path + '/foobar')
// .set('Authorization', 'Bearer ' + server.accessToken)
// .set('Accept', 'application/json')
// .expect(400)
// })
//
// it('Should fail we do not follow this server', async function () {
// await request(server.url)
// .delete(path + '/-1')
// .set('Authorization', 'Bearer ' + server.accessToken)
// .set('Accept', 'application/json')
// .expect(404)
// })
//
// it('Should succeed with the correct parameters')
})
})
after(async function () {
killallServers([ server ])
// Keep the logs if the test failed
if (this['ok']) {
await flushTests()
}
})
})

View File

@ -1,8 +1,6 @@
// Order of the tests we want to execute // Order of the tests we want to execute
import './pods' import './follows'
import './remotes'
import './users' import './users'
import './request-schedulers'
import './services' import './services'
import './videos' import './videos'
import './video-abuses' import './video-abuses'

View File

@ -1,287 +0,0 @@
/* tslint:disable:no-unused-expression */
import * as request from 'supertest'
import 'mocha'
import {
ServerInfo,
flushTests,
runServer,
createUser,
loginAndGetAccessToken,
setAccessTokensToServers,
killallServers,
makePostBodyRequest
} from '../../utils'
describe('Test pods API validators', function () {
let server: ServerInfo
// ---------------------------------------------------------------
before(async function () {
this.timeout(45000)
await flushTests()
server = await runServer(1)
await setAccessTokensToServers([ server ])
})
describe('When managing friends', function () {
const path = '/api/v1/pods/'
let userAccessToken = null
before(async function () {
await createUser(server.url, server.accessToken, 'user1', 'password')
server.user = {
username: 'user1',
password: 'password'
}
userAccessToken = await loginAndGetAccessToken(server)
})
describe('When making friends', function () {
const body = {
hosts: [ 'localhost:9002' ]
}
it('Should fail without hosts', async function () {
await request(server.url)
.post(path + '/make-friends')
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if hosts is not an array', async function () {
await request(server.url)
.post(path + '/make-friends')
.send({ hosts: 'localhost:9002' })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if the array is not composed by hosts', async function () {
await request(server.url)
.post(path + '/make-friends')
.send({ hosts: [ 'localhost:9002', 'localhost:coucou' ] })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if the array is composed with http schemes', async function () {
await request(server.url)
.post(path + '/make-friends')
.send({ hosts: [ 'localhost:9002', 'http://localhost:9003' ] })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if hosts are not unique', async function () {
await request(server.url)
.post(path + '/make-friends')
.send({ urls: [ 'localhost:9002', 'localhost:9002' ] })
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with an invalid token', async function () {
await request(server.url)
.post(path + '/make-friends')
.send(body)
.set('Authorization', 'Bearer fake_token')
.set('Accept', 'application/json')
.expect(401)
})
it('Should fail if the user is not an administrator', async function () {
await request(server.url)
.post(path + '/make-friends')
.send(body)
.set('Authorization', 'Bearer ' + userAccessToken)
.set('Accept', 'application/json')
.expect(403)
})
})
describe('When listing friends', function () {
it('Should fail with a bad start pagination', async function () {
await request(server.url)
.get(path)
.query({ start: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with a bad count pagination', async function () {
await request(server.url)
.get(path)
.query({ count: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with an incorrect sort', async function () {
await request(server.url)
.get(path)
.query({ sort: 'hello' })
.set('Accept', 'application/json')
.expect(400)
})
})
describe('When quitting friends', function () {
it('Should fail with an invalid token', async function () {
await request(server.url)
.get(path + '/quit-friends')
.query({ start: 'hello' })
.set('Authorization', 'Bearer faketoken')
.set('Accept', 'application/json')
.expect(401)
})
it('Should fail if the user is not an administrator', async function () {
await request(server.url)
.get(path + '/quit-friends')
.query({ start: 'hello' })
.set('Authorization', 'Bearer ' + userAccessToken)
.set('Accept', 'application/json')
.expect(403)
})
})
describe('When removing one friend', function () {
it('Should fail with an invalid token', async function () {
await request(server.url)
.delete(path + '/1')
.set('Authorization', 'Bearer faketoken')
.set('Accept', 'application/json')
.expect(401)
})
it('Should fail if the user is not an administrator', async function () {
await request(server.url)
.delete(path + '/1')
.set('Authorization', 'Bearer ' + userAccessToken)
.set('Accept', 'application/json')
.expect(403)
})
it('Should fail with an undefined id', async function () {
await request(server.url)
.delete(path + '/' + undefined)
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail with an invalid id', async function () {
await request(server.url)
.delete(path + '/foobar')
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(400)
})
it('Should fail if the pod is not a friend', async function () {
await request(server.url)
.delete(path + '/-1')
.set('Authorization', 'Bearer ' + server.accessToken)
.set('Accept', 'application/json')
.expect(404)
})
it('Should succeed with the correct parameters')
})
})
describe('When adding a pod from remote', function () {
const path = '/api/v1/remote/pods/add'
it('Should fail with nothing', async function () {
const fields = {}
await makePostBodyRequest({ url: server.url, path, fields })
})
it('Should fail without public key', async function () {
const fields = {
email: 'test.example.com',
host: 'coucou.com'
}
await makePostBodyRequest({ url: server.url, path, fields })
})
it('Should fail without an email', async function () {
const fields = {
host: 'coucou.com',
publicKey: 'my super public key'
}
await makePostBodyRequest({ url: server.url, path, fields })
})
it('Should fail without an invalid email', async function () {
const fields = {
host: 'coucou.com',
email: 'test.example.com',
publicKey: 'my super public key'
}
await makePostBodyRequest({ url: server.url, path, fields })
})
it('Should fail without a host', async function () {
const fields = {
email: 'test.example.com',
publicKey: 'my super public key'
}
await makePostBodyRequest({ url: server.url, path, fields })
})
it('Should fail with an incorrect host', async function () {
const fields = {
host: 'http://coucou.com',
email: 'test.example.com',
publicKey: 'my super public key'
}
await makePostBodyRequest({ url: server.url, path, fields })
fields.host = 'http://coucou'
await makePostBodyRequest({ url: server.url, path, fields })
fields.host = 'coucou'
await makePostBodyRequest({ url: server.url, path, fields })
})
it('Should succeed with the correct parameters', async function () {
const fields = {
host: 'coucou.com',
email: 'test@example.com',
publicKey: 'my super public key'
}
await makePostBodyRequest({ url: server.url, path, fields, statusCodeExpected: 200 })
})
it('Should fail with a host that already exists', async function () {
const fields = {
host: 'coucou.com',
email: 'test@example.com',
publicKey: 'my super public key'
}
await makePostBodyRequest({ url: server.url, path, fields, statusCodeExpected: 409 })
})
})
after(async function () {
killallServers([ server ])
// Keep the logs if the test failed
if (this['ok']) {
await flushTests()
}
})
})

View File

@ -1,54 +0,0 @@
/* tslint:disable:no-unused-expression */
import 'mocha'
import {
ServerInfo,
flushTests,
runServer,
setAccessTokensToServers,
killallServers
} from '../../utils'
describe('Test remote videos API validators', function () {
let server: ServerInfo
// ---------------------------------------------------------------
before(async function () {
this.timeout(60000)
await flushTests()
server = await runServer(1)
await setAccessTokensToServers([ server ])
})
describe('When making a secure request', async function () {
it('Should check a secure request')
})
describe('When adding a video', async function () {
it('Should check when adding a video')
it('Should not add an existing uuid')
})
describe('When removing a video', async function () {
it('Should check when removing a video')
})
describe('When reporting abuse on a video', async function () {
it('Should check when reporting a video abuse')
})
after(async function () {
killallServers([ server ])
// Keep the logs if the test failed
if (this['ok']) {
await flushTests()
}
})
})

View File

@ -1,65 +0,0 @@
/* tslint:disable:no-unused-expression */
import * as request from 'supertest'
import 'mocha'
import {
flushTests,
runServer,
createUser,
setAccessTokensToServers,
killallServers,
getUserAccessToken
} from '../../utils'
describe('Test request schedulers stats API validators', function () {
const path = '/api/v1/request-schedulers/stats'
let server = null
let userAccessToken = null
// ---------------------------------------------------------------
before(async function () {
this.timeout(60000)
await flushTests()
server = await runServer(1)
await setAccessTokensToServers([ server ])
const username = 'user'
const password = 'my super password'
await createUser(server.url, server.accessToken, username, password)
const user = {
username: 'user',
password: 'my super password'
}
userAccessToken = await getUserAccessToken(server, user)
})
it('Should fail with an non authenticated user', async function () {
await request(server.url)
.get(path)
.set('Accept', 'application/json')
.expect(401)
})
it('Should fail with a non admin user', async function () {
await request(server.url)
.get(path)
.set('Authorization', 'Bearer ' + userAccessToken)
.set('Accept', 'application/json')
.expect(403)
})
after(async function () {
killallServers([ server ])
// Keep the logs if the test failed
if (this['ok']) {
await flushTests()
}
})
})

View File

@ -473,7 +473,7 @@ describe('Test videos API validator', function () {
it('Should fail with a video of another user') it('Should fail with a video of another user')
it('Should fail with a video of another pod') it('Should fail with a video of another server')
it('Should succeed with the correct parameters', async function () { it('Should succeed with the correct parameters', async function () {
const fields = getCompleteVideoUpdateAttributes() const fields = getCompleteVideoUpdateAttributes()
@ -584,7 +584,7 @@ describe('Test videos API validator', function () {
it('Should fail with a video of another user') it('Should fail with a video of another user')
it('Should fail with a video of another pod') it('Should fail with a video of another server')
it('Should succeed with the correct parameters') it('Should succeed with the correct parameters')
}) })

View File

@ -2,7 +2,7 @@
import './config' import './config'
import './check-params' import './check-params'
import './users' import './users'
import './single-pod' import './single-server'
import './video-abuse' import './video-abuse'
import './video-blacklist' import './video-blacklist'
import './video-blacklist-management' import './video-blacklist-management'

View File

@ -1,3 +1,3 @@
// Order of the tests we want to execute // Order of the tests we want to execute
import './multiple-pods' import './multiple-servers'
import './video-transcoder' import './video-transcoder'

View File

@ -10,7 +10,6 @@ import {
getVideo, getVideo,
getVideosList, getVideosList,
killallServers, killallServers,
makeFriends,
rateVideo, rateVideo,
removeVideo, removeVideo,
ServerInfo, ServerInfo,
@ -22,13 +21,14 @@ import {
webtorrentAdd, webtorrentAdd,
addVideoChannel, addVideoChannel,
getVideoChannelsList, getVideoChannelsList,
getUserAccessToken getUserAccessToken,
doubleFollow
} from '../utils' } from '../utils'
import { createUser } from '../utils/users' import { createUser } from '../utils/users'
const expect = chai.expect const expect = chai.expect
describe('Test multiple pods', function () { describe('Test multiple servers', function () {
let servers: ServerInfo[] = [] let servers: ServerInfo[] = []
const toRemove = [] const toRemove = []
let videoUUID = '' let videoUUID = ''
@ -50,17 +50,15 @@ describe('Test multiple pods', function () {
const channelRes = await getVideoChannelsList(servers[0].url, 0, 1) const channelRes = await getVideoChannelsList(servers[0].url, 0, 1)
videoChannelId = channelRes.body.data[0].id videoChannelId = channelRes.body.data[0].id
// The second pod make friend with the third // Server 1 and server 2 follow each other
await makeFriends(servers[1].url, servers[1].accessToken) await doubleFollow(servers[0], servers[1])
// Server 1 and server 3 follow each other
// Wait for the request between pods await doubleFollow(servers[0], servers[2])
await wait(10000) // Server 2 and server 3 follow each other
await doubleFollow(servers[1], servers[2])
// Pod 1 make friends too
await makeFriends(servers[0].url, servers[0].accessToken)
}) })
it('Should not have videos for all pods', async function () { it('Should not have videos for all servers', async function () {
for (const server of servers) { for (const server of servers) {
const res = await getVideosList(server.url) const res = await getVideosList(server.url)
const videos = res.body.data const videos = res.body.data
@ -69,18 +67,18 @@ describe('Test multiple pods', function () {
} }
}) })
describe('Should upload the video and propagate on each pod', function () { describe('Should upload the video and propagate on each server', function () {
it('Should upload the video on pod 1 and propagate on each pod', async function () { it('Should upload the video on server 1 and propagate on each server', async function () {
// Pod 1 has video transcoding activated // Server 1 has video transcoding activated
this.timeout(15000) this.timeout(15000)
const videoAttributes = { const videoAttributes = {
name: 'my super name for pod 1', name: 'my super name for server 1',
category: 5, category: 5,
licence: 4, licence: 4,
language: 9, language: 9,
nsfw: true, nsfw: true,
description: 'my super description for pod 1', description: 'my super description for server 1',
tags: [ 'tag1p1', 'tag2p1' ], tags: [ 'tag1p1', 'tag2p1' ],
channelId: videoChannelId, channelId: videoChannelId,
fixture: 'video_short1.webm' fixture: 'video_short1.webm'
@ -89,7 +87,7 @@ describe('Test multiple pods', function () {
await wait(11000) await wait(11000)
// All pods should have this video // All servers should have this video
for (const server of servers) { for (const server of servers) {
let baseMagnet = null let baseMagnet = null
@ -99,7 +97,7 @@ describe('Test multiple pods', function () {
expect(videos).to.be.an('array') expect(videos).to.be.an('array')
expect(videos.length).to.equal(1) expect(videos.length).to.equal(1)
const video = videos[0] const video = videos[0]
expect(video.name).to.equal('my super name for pod 1') expect(video.name).to.equal('my super name for server 1')
expect(video.category).to.equal(5) expect(video.category).to.equal(5)
expect(video.categoryLabel).to.equal('Sports') expect(video.categoryLabel).to.equal('Sports')
expect(video.licence).to.equal(4) expect(video.licence).to.equal(4)
@ -107,8 +105,8 @@ describe('Test multiple pods', function () {
expect(video.language).to.equal(9) expect(video.language).to.equal(9)
expect(video.languageLabel).to.equal('Japanese') expect(video.languageLabel).to.equal('Japanese')
expect(video.nsfw).to.be.ok expect(video.nsfw).to.be.ok
expect(video.description).to.equal('my super description for pod 1') expect(video.description).to.equal('my super description for server 1')
expect(video.podHost).to.equal('localhost:9001') expect(video.serverHost).to.equal('localhost:9001')
expect(video.duration).to.equal(10) expect(video.duration).to.equal(10)
expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ]) expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ])
expect(dateIsValid(video.createdAt)).to.be.true expect(dateIsValid(video.createdAt)).to.be.true
@ -127,8 +125,9 @@ describe('Test multiple pods', function () {
const file = videoDetails.files[0] const file = videoDetails.files[0]
const magnetUri = file.magnetUri const magnetUri = file.magnetUri
expect(file.magnetUri).to.have.lengthOf.above(2) expect(file.magnetUri).to.have.lengthOf.above(2)
expect(file.torrentUrl).to.equal(`http://${videoDetails.podHost}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`) expect(file.torrentUrl).to
expect(file.fileUrl).to.equal(`http://${videoDetails.podHost}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`) .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.resolution).to.equal(720)
expect(file.resolutionLabel).to.equal('720p') expect(file.resolutionLabel).to.equal('720p')
expect(file.size).to.equal(572456) expect(file.size).to.equal(572456)
@ -141,7 +140,7 @@ describe('Test multiple pods', function () {
expect(videoDetails.channel.isLocal).to.be.true expect(videoDetails.channel.isLocal).to.be.true
} }
// All pods should have the same magnet Uri // All servers should have the same magnet Uri
if (baseMagnet === null) { if (baseMagnet === null) {
baseMagnet = magnetUri baseMagnet = magnetUri
} else { } else {
@ -153,7 +152,7 @@ describe('Test multiple pods', function () {
} }
}) })
it('Should upload the video on pod 2 and propagate on each pod', async function () { it('Should upload the video on server 2 and propagate on each server', async function () {
this.timeout(120000) this.timeout(120000)
const user = { const user = {
@ -164,12 +163,12 @@ describe('Test multiple pods', function () {
const userAccessToken = await getUserAccessToken(servers[1], user) const userAccessToken = await getUserAccessToken(servers[1], user)
const videoAttributes = { const videoAttributes = {
name: 'my super name for pod 2', name: 'my super name for server 2',
category: 4, category: 4,
licence: 3, licence: 3,
language: 11, language: 11,
nsfw: true, nsfw: true,
description: 'my super description for pod 2', description: 'my super description for server 2',
tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
fixture: 'video_short2.webm' fixture: 'video_short2.webm'
} }
@ -178,7 +177,7 @@ describe('Test multiple pods', function () {
// Transcoding, so wait more than 22000 // Transcoding, so wait more than 22000
await wait(60000) await wait(60000)
// All pods should have this video // All servers should have this video
for (const server of servers) { for (const server of servers) {
let baseMagnet = {} let baseMagnet = {}
@ -188,7 +187,7 @@ describe('Test multiple pods', function () {
expect(videos).to.be.an('array') expect(videos).to.be.an('array')
expect(videos.length).to.equal(2) expect(videos.length).to.equal(2)
const video = videos[1] const video = videos[1]
expect(video.name).to.equal('my super name for pod 2') expect(video.name).to.equal('my super name for server 2')
expect(video.category).to.equal(4) expect(video.category).to.equal(4)
expect(video.categoryLabel).to.equal('Art') expect(video.categoryLabel).to.equal('Art')
expect(video.licence).to.equal(3) expect(video.licence).to.equal(3)
@ -196,8 +195,8 @@ describe('Test multiple pods', function () {
expect(video.language).to.equal(11) expect(video.language).to.equal(11)
expect(video.languageLabel).to.equal('German') expect(video.languageLabel).to.equal('German')
expect(video.nsfw).to.be.true expect(video.nsfw).to.be.true
expect(video.description).to.equal('my super description for pod 2') expect(video.description).to.equal('my super description for server 2')
expect(video.podHost).to.equal('localhost:9002') expect(video.serverHost).to.equal('localhost:9002')
expect(video.duration).to.equal(5) expect(video.duration).to.equal(5)
expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ]) expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ])
expect(dateIsValid(video.createdAt)).to.be.true expect(dateIsValid(video.createdAt)).to.be.true
@ -223,7 +222,7 @@ describe('Test multiple pods', function () {
for (const file of videoDetails.files) { for (const file of videoDetails.files) {
expect(file.magnetUri).to.have.lengthOf.above(2) expect(file.magnetUri).to.have.lengthOf.above(2)
// All pods should have the same magnet Uri // All servers should have the same magnet Uri
if (baseMagnet[file.resolution] === undefined) { if (baseMagnet[file.resolution] === undefined) {
baseMagnet[file.resolution] = file.magnet baseMagnet[file.resolution] = file.magnet
} else { } else {
@ -256,28 +255,28 @@ describe('Test multiple pods', function () {
} }
}) })
it('Should upload two videos on pod 3 and propagate on each pod', async function () { it('Should upload two videos on server 3 and propagate on each server', async function () {
this.timeout(45000) this.timeout(45000)
const videoAttributes1 = { const videoAttributes1 = {
name: 'my super name for pod 3', name: 'my super name for server 3',
category: 6, category: 6,
licence: 5, licence: 5,
language: 11, language: 11,
nsfw: true, nsfw: true,
description: 'my super description for pod 3', description: 'my super description for server 3',
tags: [ 'tag1p3' ], tags: [ 'tag1p3' ],
fixture: 'video_short3.webm' fixture: 'video_short3.webm'
} }
await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1) await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1)
const videoAttributes2 = { const videoAttributes2 = {
name: 'my super name for pod 3-2', name: 'my super name for server 3-2',
category: 7, category: 7,
licence: 6, licence: 6,
language: 12, language: 12,
nsfw: false, nsfw: false,
description: 'my super description for pod 3-2', description: 'my super description for server 3-2',
tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
fixture: 'video_short.webm' fixture: 'video_short.webm'
} }
@ -286,7 +285,7 @@ describe('Test multiple pods', function () {
await wait(33000) await wait(33000)
let baseMagnet = null let baseMagnet = null
// All pods should have this video // All servers should have this video
for (const server of servers) { for (const server of servers) {
const res = await getVideosList(server.url) const res = await getVideosList(server.url)
@ -297,7 +296,7 @@ describe('Test multiple pods', function () {
// We not sure about the order of the two last uploads // We not sure about the order of the two last uploads
let video1 = null let video1 = null
let video2 = null let video2 = null
if (videos[2].name === 'my super name for pod 3') { if (videos[2].name === 'my super name for server 3') {
video1 = videos[2] video1 = videos[2]
video2 = videos[3] video2 = videos[3]
} else { } else {
@ -305,7 +304,7 @@ describe('Test multiple pods', function () {
video2 = videos[2] video2 = videos[2]
} }
expect(video1.name).to.equal('my super name for pod 3') expect(video1.name).to.equal('my super name for server 3')
expect(video1.category).to.equal(6) expect(video1.category).to.equal(6)
expect(video1.categoryLabel).to.equal('Travels') expect(video1.categoryLabel).to.equal('Travels')
expect(video1.licence).to.equal(5) expect(video1.licence).to.equal(5)
@ -313,8 +312,8 @@ describe('Test multiple pods', function () {
expect(video1.language).to.equal(11) expect(video1.language).to.equal(11)
expect(video1.languageLabel).to.equal('German') expect(video1.languageLabel).to.equal('German')
expect(video1.nsfw).to.be.ok expect(video1.nsfw).to.be.ok
expect(video1.description).to.equal('my super description for pod 3') expect(video1.description).to.equal('my super description for server 3')
expect(video1.podHost).to.equal('localhost:9003') expect(video1.serverHost).to.equal('localhost:9003')
expect(video1.duration).to.equal(5) expect(video1.duration).to.equal(5)
expect(video1.tags).to.deep.equal([ 'tag1p3' ]) expect(video1.tags).to.deep.equal([ 'tag1p3' ])
expect(video1.author).to.equal('root') expect(video1.author).to.equal('root')
@ -331,7 +330,7 @@ describe('Test multiple pods', function () {
expect(file1.resolutionLabel).to.equal('720p') expect(file1.resolutionLabel).to.equal('720p')
expect(file1.size).to.equal(292677) expect(file1.size).to.equal(292677)
expect(video2.name).to.equal('my super name for pod 3-2') expect(video2.name).to.equal('my super name for server 3-2')
expect(video2.category).to.equal(7) expect(video2.category).to.equal(7)
expect(video2.categoryLabel).to.equal('Gaming') expect(video2.categoryLabel).to.equal('Gaming')
expect(video2.licence).to.equal(6) expect(video2.licence).to.equal(6)
@ -339,8 +338,8 @@ describe('Test multiple pods', function () {
expect(video2.language).to.equal(12) expect(video2.language).to.equal(12)
expect(video2.languageLabel).to.equal('Korean') expect(video2.languageLabel).to.equal('Korean')
expect(video2.nsfw).to.be.false expect(video2.nsfw).to.be.false
expect(video2.description).to.equal('my super description for pod 3-2') expect(video2.description).to.equal('my super description for server 3-2')
expect(video2.podHost).to.equal('localhost:9003') expect(video2.serverHost).to.equal('localhost:9003')
expect(video2.duration).to.equal(5) expect(video2.duration).to.equal(5)
expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ]) expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
expect(video2.author).to.equal('root') expect(video2.author).to.equal('root')
@ -367,7 +366,7 @@ describe('Test multiple pods', function () {
expect(video2.isLocal).to.be.true expect(video2.isLocal).to.be.true
} }
// All pods should have the same magnet Uri // All servers should have the same magnet Uri
if (baseMagnet === null) { if (baseMagnet === null) {
baseMagnet = magnetUri2 baseMagnet = magnetUri2
} else { } else {
@ -384,7 +383,7 @@ describe('Test multiple pods', function () {
}) })
describe('Should seed the uploaded video', function () { describe('Should seed the uploaded video', function () {
it('Should add the file 1 by asking pod 3', async function () { it('Should add the file 1 by asking server 3', async function () {
// Yes, this could be long // Yes, this could be long
this.timeout(200000) this.timeout(200000)
@ -403,7 +402,7 @@ describe('Test multiple pods', function () {
expect(torrent.files[0].path).to.exist.and.to.not.equal('') expect(torrent.files[0].path).to.exist.and.to.not.equal('')
}) })
it('Should add the file 2 by asking pod 1', async function () { it('Should add the file 2 by asking server 1', async function () {
// Yes, this could be long // Yes, this could be long
this.timeout(200000) this.timeout(200000)
@ -419,7 +418,7 @@ describe('Test multiple pods', function () {
expect(torrent.files[0].path).to.exist.and.to.not.equal('') expect(torrent.files[0].path).to.exist.and.to.not.equal('')
}) })
it('Should add the file 3 by asking pod 2', async function () { it('Should add the file 3 by asking server 2', async function () {
// Yes, this could be long // Yes, this could be long
this.timeout(200000) this.timeout(200000)
@ -435,7 +434,7 @@ describe('Test multiple pods', function () {
expect(torrent.files[0].path).to.exist.and.to.not.equal('') expect(torrent.files[0].path).to.exist.and.to.not.equal('')
}) })
it('Should add the file 3-2 by asking pod 1', async function () { it('Should add the file 3-2 by asking server 1', async function () {
// Yes, this could be long // Yes, this could be long
this.timeout(200000) this.timeout(200000)
@ -451,13 +450,13 @@ describe('Test multiple pods', function () {
expect(torrent.files[0].path).to.exist.and.to.not.equal('') expect(torrent.files[0].path).to.exist.and.to.not.equal('')
}) })
it('Should add the file 2 in 360p by asking pod 1', async function () { it('Should add the file 2 in 360p by asking server 1', async function () {
// Yes, this could be long // Yes, this could be long
this.timeout(200000) this.timeout(200000)
const res = await getVideosList(servers[0].url) const res = await getVideosList(servers[0].url)
const video = res.body.data.find(v => v.name === 'my super name for pod 2') 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 res2 = await getVideo(servers[0].url, video.id)
const videoDetails = res2.body const videoDetails = res2.body
@ -472,31 +471,31 @@ describe('Test multiple pods', function () {
}) })
describe('Should update video views, likes and dislikes', function () { describe('Should update video views, likes and dislikes', function () {
let localVideosPod3 = [] let localVideosServer3 = []
let remoteVideosPod1 = [] let remoteVideosServer1 = []
let remoteVideosPod2 = [] let remoteVideosServer2 = []
let remoteVideosPod3 = [] let remoteVideosServer3 = []
before(async function () { before(async function () {
const res1 = await getVideosList(servers[0].url) const res1 = await getVideosList(servers[0].url)
remoteVideosPod1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid) remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
const res2 = await getVideosList(servers[1].url) const res2 = await getVideosList(servers[1].url)
remoteVideosPod2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid) remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
const res3 = await getVideosList(servers[2].url) const res3 = await getVideosList(servers[2].url)
localVideosPod3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid) localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid)
remoteVideosPod3 = res3.body.data.filter(video => video.isLocal === false).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 () { it('Should view multiple videos on owned servers', async function () {
this.timeout(30000) this.timeout(30000)
const tasks: Promise<any>[] = [] const tasks: Promise<any>[] = []
tasks.push(getVideo(servers[2].url, localVideosPod3[0])) tasks.push(getVideo(servers[2].url, localVideosServer3[0]))
tasks.push(getVideo(servers[2].url, localVideosPod3[0])) tasks.push(getVideo(servers[2].url, localVideosServer3[0]))
tasks.push(getVideo(servers[2].url, localVideosPod3[0])) tasks.push(getVideo(servers[2].url, localVideosServer3[0]))
tasks.push(getVideo(servers[2].url, localVideosPod3[1])) tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
await Promise.all(tasks) await Promise.all(tasks)
@ -506,8 +505,8 @@ describe('Test multiple pods', function () {
const res = await getVideosList(server.url) const res = await getVideosList(server.url)
const videos = res.body.data const videos = res.body.data
const video0 = videos.find(v => v.uuid === localVideosPod3[0]) const video0 = videos.find(v => v.uuid === localVideosServer3[0])
const video1 = videos.find(v => v.uuid === localVideosPod3[1]) const video1 = videos.find(v => v.uuid === localVideosServer3[1])
expect(video0.views).to.equal(7) expect(video0.views).to.equal(7)
expect(video1.views).to.equal(5) expect(video1.views).to.equal(5)
@ -518,16 +517,16 @@ describe('Test multiple pods', function () {
this.timeout(30000) this.timeout(30000)
const tasks: Promise<any>[] = [] const tasks: Promise<any>[] = []
tasks.push(getVideo(servers[0].url, remoteVideosPod1[0])) tasks.push(getVideo(servers[0].url, remoteVideosServer1[0]))
tasks.push(getVideo(servers[1].url, remoteVideosPod2[0])) tasks.push(getVideo(servers[1].url, remoteVideosServer2[0]))
tasks.push(getVideo(servers[1].url, remoteVideosPod2[0])) tasks.push(getVideo(servers[1].url, remoteVideosServer2[0]))
tasks.push(getVideo(servers[2].url, remoteVideosPod3[0])) tasks.push(getVideo(servers[2].url, remoteVideosServer3[0]))
tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) tasks.push(getVideo(servers[2].url, remoteVideosServer3[1]))
tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) tasks.push(getVideo(servers[2].url, remoteVideosServer3[1]))
tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) tasks.push(getVideo(servers[2].url, remoteVideosServer3[1]))
tasks.push(getVideo(servers[2].url, localVideosPod3[1])) tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
tasks.push(getVideo(servers[2].url, localVideosPod3[1])) tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
tasks.push(getVideo(servers[2].url, localVideosPod3[1])) tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
await Promise.all(tasks) await Promise.all(tasks)
@ -557,13 +556,13 @@ describe('Test multiple pods', function () {
this.timeout(30000) this.timeout(30000)
const tasks: Promise<any>[] = [] const tasks: Promise<any>[] = []
tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like')) tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'dislike')) tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike'))
tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like')) tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'like')) tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like'))
tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'dislike')) tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike'))
tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[1], 'dislike')) tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike'))
tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[0], 'like')) tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like'))
await Promise.all(tasks) await Promise.all(tasks)
@ -591,7 +590,7 @@ describe('Test multiple pods', function () {
}) })
describe('Should manipulate these videos', function () { describe('Should manipulate these videos', function () {
it('Should update the video 3 by asking pod 3', async function () { it('Should update the video 3 by asking server 3', async function () {
this.timeout(15000) this.timeout(15000)
const attributes = { const attributes = {
@ -609,7 +608,7 @@ describe('Test multiple pods', function () {
await wait(11000) await wait(11000)
}) })
it('Should have the video 3 updated on each pod', async function () { it('Should have the video 3 updated on each server', async function () {
this.timeout(200000) this.timeout(200000)
for (const server of servers) { for (const server of servers) {
@ -651,7 +650,7 @@ describe('Test multiple pods', function () {
} }
}) })
it('Should remove the videos 3 and 3-2 by asking pod 3', async function () { it('Should remove the videos 3 and 3-2 by asking server 3', async function () {
this.timeout(15000) this.timeout(15000)
await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id) await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id)
@ -660,7 +659,7 @@ describe('Test multiple pods', function () {
await wait(11000) await wait(11000)
}) })
it('Should have videos 1 and 3 on each pod', async function () { it('Should have videos 1 and 3 on each server', async function () {
for (const server of servers) { for (const server of servers) {
const res = await getVideosList(server.url) const res = await getVideosList(server.url)
@ -673,11 +672,11 @@ describe('Test multiple pods', function () {
expect(videos[0].name).not.to.equal(toRemove[1].name) expect(videos[0].name).not.to.equal(toRemove[1].name)
expect(videos[1].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 pod 1').uuid videoUUID = videos.find(video => video.name === 'my super name for server 1').uuid
} }
}) })
it('Should get the same video by UUID on each pod', async function () { it('Should get the same video by UUID on each server', async function () {
let baseVideo = null let baseVideo = null
for (const server of servers) { for (const server of servers) {
const res = await getVideo(server.url, videoUUID) const res = await getVideo(server.url, videoUUID)
@ -701,7 +700,7 @@ describe('Test multiple pods', function () {
} }
}) })
it('Should get the preview from each pod', async function () { it('Should get the preview from each server', async function () {
for (const server of servers) { for (const server of servers) {
const res = await getVideo(server.url, videoUUID) const res = await getVideo(server.url, videoUUID)
const video = res.body const video = res.body

View File

@ -14,7 +14,6 @@ import {
getOEmbed getOEmbed
} from '../utils' } from '../utils'
import { runServer } from '../utils/servers' import { runServer } from '../utils/servers'
import { Video } from '../../../client/src/app/videos/shared/video.model'
describe('Test services', function () { describe('Test services', function () {
let server: ServerInfo = null let server: ServerInfo = null

View File

@ -34,7 +34,7 @@ import {
updateVideo updateVideo
} from '../utils' } from '../utils'
describe('Test a single pod', function () { describe('Test a single server', function () {
let server: ServerInfo = null let server: ServerInfo = null
let videoId = -1 let videoId = -1
let videoUUID = '' let videoUUID = ''

View File

@ -40,14 +40,14 @@ describe('Test video privacy', function () {
}) })
it('Should upload a private video on server 1', async function () { it('Should upload a private video on server 1', async function () {
this.timeout(15000) this.timeout(25000)
const attributes = { const attributes = {
privacy: VideoPrivacy.PRIVATE privacy: VideoPrivacy.PRIVATE
} }
await uploadVideo(servers[0].url, servers[0].accessToken, attributes) await uploadVideo(servers[0].url, servers[0].accessToken, attributes)
await wait(11000) await wait(15000)
}) })
it('Should not have this private video on server 2', async function () { it('Should not have this private video on server 2', async function () {
@ -86,8 +86,8 @@ describe('Test video privacy', function () {
await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID) await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID)
}) })
it('Should upload a unlisted video on server 2', async function () { it('Should upload an unlisted video on server 2', async function () {
this.timeout(30000) this.timeout(50000)
const attributes = { const attributes = {
name: 'unlisted video', name: 'unlisted video',
@ -95,7 +95,7 @@ describe('Test video privacy', function () {
} }
await uploadVideo(servers[1].url, servers[1].accessToken, attributes) await uploadVideo(servers[1].url, servers[1].accessToken, attributes)
await wait(22000) await wait(40000)
}) })
it('Should not have this unlisted video listed on server 1 and 2', async function () { it('Should not have this unlisted video listed on server 1 and 2', async function () {
@ -125,7 +125,7 @@ describe('Test video privacy', function () {
}) })
it('Should update the private video to public on server 1', async function () { it('Should update the private video to public on server 1', async function () {
this.timeout(15000) this.timeout(40000)
const attribute = { const attribute = {
name: 'super video public', name: 'super video public',
@ -134,10 +134,10 @@ describe('Test video privacy', function () {
await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute) await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute)
await wait(11000) await wait(30000)
}) })
it('Should not have this new unlisted video listed on server 1 and 2', async function () { it('Should have this new public video listed on server 1 and 2', async function () {
for (const server of servers) { for (const server of servers) {
const res = await getVideosList(server.url) const res = await getVideosList(server.url)

View File

@ -26,8 +26,8 @@ describe('Test a client controllers', function () {
server.accessToken = await loginAndGetAccessToken(server) server.accessToken = await loginAndGetAccessToken(server)
const videoAttributes = { const videoAttributes = {
name: 'my super name for pod 1', name: 'my super name for server 1',
description: 'my super description for pod 1' description: 'my super description for server 1'
} }
await uploadVideo(server.url, server.accessToken, videoAttributes) await uploadVideo(server.url, server.accessToken, videoAttributes)
@ -44,8 +44,8 @@ describe('Test a client controllers', function () {
.get('/videos/watch/' + server.video.id) .get('/videos/watch/' + server.video.id)
.expect(200) .expect(200)
expect(res.text).to.contain('<meta property="og:title" content="my super name for pod 1" />') expect(res.text).to.contain('<meta property="og:title" content="my super name for server 1" />')
expect(res.text).to.contain('<meta property="og:description" content="my super description for pod 1" />') expect(res.text).to.contain('<meta property="og:description" content="my super description for server 1" />')
}) })
it('Should have valid Open Graph tags on the watch page with video uuid', async function () { it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
@ -53,8 +53,8 @@ describe('Test a client controllers', function () {
.get('/videos/watch/' + server.video.uuid) .get('/videos/watch/' + server.video.uuid)
.expect(200) .expect(200)
expect(res.text).to.contain('<meta property="og:title" content="my super name for pod 1" />') expect(res.text).to.contain('<meta property="og:title" content="my super name for server 1" />')
expect(res.text).to.contain('<meta property="og:description" content="my super description for pod 1" />') expect(res.text).to.contain('<meta property="og:description" content="my super description for server 1" />')
}) })
it('Should have valid oEmbed discovery tags', async function () { it('Should have valid oEmbed discovery tags', async function () {

View File

@ -1,25 +1,24 @@
import * as program from 'commander'
// /!\ Before imports /!\ // /!\ Before imports /!\
process.env.NODE_ENV = 'test' process.env.NODE_ENV = 'test'
import { Video, VideoRateType, VideoFile } from '../../../shared' import * as program from 'commander'
import { Video, VideoFile, VideoRateType } from '../../../shared'
import { import {
ServerInfo as DefaultServerInfo,
flushAndRunMultipleServers, flushAndRunMultipleServers,
setAccessTokensToServers,
makeFriends,
wait,
killallServers,
flushTests, flushTests,
uploadVideo,
getVideosList,
updateVideo,
removeVideo,
getVideo,
getAllVideosListBy, getAllVideosListBy,
getRequestsStats getRequestsStats,
getVideo,
getVideosList,
killallServers,
removeVideo,
ServerInfo as DefaultServerInfo,
setAccessTokensToServers,
updateVideo,
uploadVideo,
wait
} from '../utils' } from '../utils'
import { follow } from '../utils/follows'
interface ServerInfo extends DefaultServerInfo { interface ServerInfo extends DefaultServerInfo {
requestsNumber: number requestsNumber: number
@ -32,7 +31,7 @@ program
.option('-v, --view [weight]', 'Weight for viewing videos') .option('-v, --view [weight]', 'Weight for viewing videos')
.option('-l, --like [weight]', 'Weight for liking videos') .option('-l, --like [weight]', 'Weight for liking videos')
.option('-s, --dislike [weight]', 'Weight for disliking videos') .option('-s, --dislike [weight]', 'Weight for disliking videos')
.option('-p, --pods [n]', 'Number of pods to run (3 or 6)', /^3|6$/, 3) .option('-p, --servers [n]', 'Number of servers to run (3 or 6)', /^3|6$/, 3)
.option('-i, --interval-action [interval]', 'Interval in ms for an action') .option('-i, --interval-action [interval]', 'Interval in ms for an action')
.option('-I, --interval-integrity [interval]', 'Interval in ms for an integrity check') .option('-I, --interval-integrity [interval]', 'Interval in ms for an integrity check')
.option('-f, --flush', 'Flush datas on exit') .option('-f, --flush', 'Flush datas on exit')
@ -50,7 +49,7 @@ const actionInterval = program['intervalAction'] !== undefined ? parseInt(progra
const integrityInterval = program['intervalIntegrity'] !== undefined ? parseInt(program['intervalIntegrity'], 10) : 60000 const integrityInterval = program['intervalIntegrity'] !== undefined ? parseInt(program['intervalIntegrity'], 10) : 60000
const displayDiffOnFail = program['difference'] || false const displayDiffOnFail = program['difference'] || false
const numberOfPods = 6 const numberOfServers = 6
console.log( console.log(
'Create weight: %d, update weight: %d, remove weight: %d, view weight: %d, like weight: %d, dislike weight: %d.', 'Create weight: %d, update weight: %d, remove weight: %d, view weight: %d, like weight: %d, dislike weight: %d.',
@ -77,7 +76,7 @@ start()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
async function start () { async function start () {
const servers = await runServers(numberOfPods) const servers = await runServers(numberOfServers)
process.on('exit', async () => { process.on('exit', async () => {
await exitServers(servers, flushAtExit) await exitServers(servers, flushAtExit)
@ -152,22 +151,20 @@ function getRandomNumServer (servers) {
return getRandomInt(0, servers.length) return getRandomInt(0, servers.length)
} }
async function runServers (numberOfPods: number) { async function runServers (numberOfServers: number) {
const servers: ServerInfo[] = (await flushAndRunMultipleServers(numberOfPods)) const servers: ServerInfo[] = (await flushAndRunMultipleServers(numberOfServers))
.map(s => Object.assign({ requestsNumber: 0 }, s)) .map(s => Object.assign({ requestsNumber: 0 }, s))
// Get the access tokens // Get the access tokens
await setAccessTokensToServers(servers) await setAccessTokensToServers(servers)
await makeFriends(servers[1].url, servers[1].accessToken) for (let i = 0; i < numberOfServers; i++) {
await makeFriends(servers[0].url, servers[0].accessToken) for (let j = 0; j < numberOfServers; j++) {
await wait(1000) if (i === j) continue
await makeFriends(servers[3].url, servers[3].accessToken) await follow(servers[i].url, [ servers[j].url ], servers[i].accessToken)
await makeFriends(servers[5].url, servers[5].accessToken) }
await makeFriends(servers[4].url, servers[4].accessToken) }
await wait(1000)
return servers return servers
} }
@ -259,7 +256,7 @@ async function checkIntegrity (servers: ServerInfo[]) {
const videos: Video[][] = [] const videos: Video[][] = []
const tasks: Promise<any>[] = [] const tasks: Promise<any>[] = []
// Fetch all videos and remove some fields that can differ between pods // Fetch all videos and remove some fields that can differ between servers
for (const server of servers) { for (const server of servers) {
const p = getAllVideosListBy(server.url).then(res => videos.push(res.body.data)) const p = getAllVideosListBy(server.url).then(res => videos.push(res.body.data))
tasks.push(p) tasks.push(p)

View File

@ -29,7 +29,7 @@ function getFollowingListPaginationAndSort (url: string, start: number, count: n
} }
async function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) { async function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) {
const path = '/api/v1/server/follow' const path = '/api/v1/server/following'
const followingHosts = following.map(f => f.replace(/^http:\/\//, '')) const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
const res = await request(follower) const res = await request(follower)

View File

@ -12,6 +12,7 @@ export interface BaseActivity {
'@context'?: any[] '@context'?: any[]
id: string id: string
to?: string[] to?: string[]
cc?: string[]
actor: string actor: string
type: ActivityType type: ActivityType
signature?: ActivityPubSignature signature?: ActivityPubSignature