From 9a27cdc27c900feaae5f6db4315c4ccdfc0c4493 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 17 Nov 2017 15:20:42 +0100 Subject: [PATCH] Optimize signature verification --- .../+admin/follows/shared/follow.service.ts | 2 +- server/controllers/api/server/follows.ts | 2 +- server/helpers/activitypub.ts | 6 +- server/helpers/custom-jsonld-signature.ts | 20 ++ .../custom-validators/activitypub/misc.ts | 4 + server/helpers/peertube-crypto.ts | 5 +- server/initializers/constants.ts | 4 +- server/lib/activitypub/misc.ts | 16 +- server/lib/activitypub/process-add.ts | 15 +- server/lib/activitypub/process-update.ts | 1 - server/lib/activitypub/send-request.ts | 37 ++- server/middlewares/activitypub.ts | 9 +- server/models/video/video.ts | 1 + server/tests/api/check-params/follows.ts | 222 ++++++++++++++ server/tests/api/check-params/index.ts | 4 +- server/tests/api/check-params/pods.ts | 287 ------------------ server/tests/api/check-params/remotes.ts | 54 ---- .../api/check-params/request-schedulers.ts | 65 ---- server/tests/api/check-params/videos.ts | 4 +- server/tests/api/index-fast.ts | 2 +- server/tests/api/index-slow.ts | 2 +- .../{multiple-pods.ts => multiple-servers.ts} | 179 ++++++----- server/tests/api/services.ts | 1 - .../api/{single-pod.ts => single-server.ts} | 2 +- server/tests/api/video-privacy.ts | 16 +- server/tests/client.ts | 12 +- server/tests/real-world/real-world.ts | 53 ++-- server/tests/utils/follows.ts | 2 +- shared/models/activitypub/activity.ts | 1 + 29 files changed, 444 insertions(+), 584 deletions(-) create mode 100644 server/helpers/custom-jsonld-signature.ts create mode 100644 server/tests/api/check-params/follows.ts delete mode 100644 server/tests/api/check-params/pods.ts delete mode 100644 server/tests/api/check-params/remotes.ts delete mode 100644 server/tests/api/check-params/request-schedulers.ts rename server/tests/api/{multiple-pods.ts => multiple-servers.ts} (80%) rename server/tests/api/{single-pod.ts => single-server.ts} (99%) diff --git a/client/src/app/+admin/follows/shared/follow.service.ts b/client/src/app/+admin/follows/shared/follow.service.ts index d64361ee3..f66ed477d 100644 --- a/client/src/app/+admin/follows/shared/follow.service.ts +++ b/client/src/app/+admin/follows/shared/follow.service.ts @@ -42,7 +42,7 @@ export class FollowService { 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) .catch(res => this.restExtractor.handleError(res)) } diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index 520d4d858..3d184ec1f 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts @@ -25,7 +25,7 @@ serverFollowsRouter.get('/following', asyncMiddleware(listFollowing) ) -serverFollowsRouter.post('/follow', +serverFollowsRouter.post('/following', authenticate, ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), followValidator, diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index 6f216e106..aff58515a 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts @@ -8,7 +8,7 @@ import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-ac import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object' import { ResultList } from '../../shared/models/result-list.model' 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 { sendVideoAnnounce } from '../lib/activitypub/send-request' import { sendVideoChannelAnnounce } from '../lib/index' @@ -99,7 +99,7 @@ async function fetchRemoteAccountAndCreateServer (accountUrl: string) { uri: accountUrl, method: 'GET', headers: { - 'Accept': ACTIVITY_PUB_ACCEPT_HEADER + 'Accept': ACTIVITY_PUB.ACCEPT_HEADER } } @@ -157,7 +157,7 @@ async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChan uri: videoChannelUrl, method: 'GET', headers: { - 'Accept': ACTIVITY_PUB_ACCEPT_HEADER + 'Accept': ACTIVITY_PUB.ACCEPT_HEADER } } diff --git a/server/helpers/custom-jsonld-signature.ts b/server/helpers/custom-jsonld-signature.ts new file mode 100644 index 000000000..afb960618 --- /dev/null +++ b/server/helpers/custom-jsonld-signature.ts @@ -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 } diff --git a/server/helpers/custom-validators/activitypub/misc.ts b/server/helpers/custom-validators/activitypub/misc.ts index f09a764b6..1bbfd0fc4 100644 --- a/server/helpers/custom-validators/activitypub/misc.ts +++ b/server/helpers/custom-validators/activitypub/misc.ts @@ -28,6 +28,10 @@ function isBaseActivityValid (activity: any, type: string) { ( activity.to === undefined || (Array.isArray(activity.to) && activity.to.every(t => isActivityPubUrlValid(t))) + ) && + ( + activity.cc === undefined || + (Array.isArray(activity.cc) && activity.cc.every(t => isActivityPubUrlValid(t))) ) } diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts index 04a8d5681..c61abfa8e 100644 --- a/server/helpers/peertube-crypto.ts +++ b/server/helpers/peertube-crypto.ts @@ -1,7 +1,3 @@ -import * as jsonld from 'jsonld' -import * as jsig from 'jsonld-signatures' -jsig.use('jsonld', jsonld) - import { PRIVATE_RSA_KEY_SIZE, BCRYPT_SALT_SIZE @@ -15,6 +11,7 @@ import { } from './core-utils' import { logger } from './logger' import { AccountInstance } from '../models/account/account-interface' +import { jsig } from './custom-jsonld-signature' async function createPrivateAndPublicKeys () { logger.info('Generating a RSA key...') diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 48d7b5b98..9c7c31a61 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -223,9 +223,10 @@ const FRIEND_SCORE = { } const SERVER_ACCOUNT_NAME = 'peertube' -const ACTIVITY_PUB_ACCEPT_HEADER = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' 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, URL_MIME_TYPES: { VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT @@ -349,7 +350,6 @@ export { SERVERS_SCORE, PREVIEWS_SIZE, REMOTE_SCHEME, - ACTIVITY_PUB_ACCEPT_HEADER, FOLLOW_STATES, SEARCHABLE_COLUMNS, SERVER_ACCOUNT_NAME, diff --git a/server/lib/activitypub/misc.ts b/server/lib/activitypub/misc.ts index c07d9f654..4c210eb10 100644 --- a/server/lib/activitypub/misc.ts +++ b/server/lib/activitypub/misc.ts @@ -2,11 +2,12 @@ import * as magnetUtil from 'magnet-uri' import { VideoTorrentObject } from '../../../shared' import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' 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 { VideoChannelInstance } from '../../models/video/video-channel-interface' import { VideoFileAttributes } from '../../models/video/video-file-interface' import { VideoAttributes, VideoInstance } from '../../models/video/video-interface' +import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum' function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) { return { @@ -23,8 +24,14 @@ function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChan async function videoActivityObjectToDBAttributes ( 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 videoData: VideoAttributes = { name: videoObject.name, @@ -43,11 +50,8 @@ async function videoActivityObjectToDBAttributes ( views: videoObject.views, likes: 0, dislikes: 0, - // likes: videoToCreateData.likes, - // dislikes: videoToCreateData.dislikes, remote: true, - privacy: 1 - // privacy: videoToCreateData.privacy + privacy } return videoData diff --git a/server/lib/activitypub/process-add.ts b/server/lib/activitypub/process-add.ts index 72c5b1932..c83d9e98e 100644 --- a/server/lib/activitypub/process-add.ts +++ b/server/lib/activitypub/process-add.ts @@ -17,7 +17,7 @@ async function processAddActivity (activity: ActivityAdd) { const videoChannelUrl = activity.target 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 }) @@ -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 = { - arguments: [ account, videoChannel, video ], + arguments: [ account, activity, videoChannel, video ], errorMessage: 'Cannot insert the remote video with many retries.' } 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) 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) 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) // Don't block on request diff --git a/server/lib/activitypub/process-update.ts b/server/lib/activitypub/process-update.ts index 4aefd1b9b..b732fce33 100644 --- a/server/lib/activitypub/process-update.ts +++ b/server/lib/activitypub/process-update.ts @@ -70,7 +70,6 @@ async function updateRemoteVideo (account: AccountInstance, videoAttributesToUpd videoInstance.set('views', videoData.views) // videoInstance.set('likes', videoData.likes) // videoInstance.set('dislikes', videoData.dislikes) - // videoInstance.set('privacy', videoData.privacy) await videoInstance.save(sequelizeOptions) diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts index 8d013fa87..68a631a36 100644 --- a/server/lib/activitypub/send-request.ts +++ b/server/lib/activitypub/send-request.ts @@ -13,6 +13,8 @@ import { database as db } from '../../initializers' import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../models' import { VideoAbuseInstance } from '../../models/video/video-abuse-interface' 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) { const byAccount = videoChannel.Account @@ -50,7 +52,7 @@ async function sendAddVideo (video: VideoInstance, t: Transaction) { const byAccount = video.VideoChannel.Account 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) } @@ -96,7 +98,7 @@ async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstan const url = getActivityPubUrl('video', video.uuid) + '#announce' 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) 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) } -async function getPublicActivityTo (account: AccountInstance) { - const inboxUrls = await account.getFollowerSharedInboxUrls() +async function getAudience (accountSender: AccountInstance, isPublic = true) { + 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) { - const to = await getPublicActivityTo(byAccount) + const { to, cc } = await getAudience(byAccount) const activity: ActivityCreate = { type: 'Create', id: url, actor: byAccount.url, to, + cc, object } @@ -187,12 +202,13 @@ async function createActivityData (url: string, byAccount: AccountInstance, obje } async function updateActivityData (url: string, byAccount: AccountInstance, object: any) { - const to = await getPublicActivityTo(byAccount) + const { to, cc } = await getAudience(byAccount) const activity: ActivityUpdate = { type: 'Update', id: url, actor: byAccount.url, to, + cc, object } @@ -209,13 +225,16 @@ async function deleteActivityData (url: string, byAccount: AccountInstance) { return activity } -async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any) { - const to = await getPublicActivityTo(byAccount) +async function addActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, target: string, object: any) { + const videoPublic = video.privacy === VideoPrivacy.PUBLIC + + const { to, cc } = await getAudience(byAccount, videoPublic) const activity: ActivityAdd = { type: 'Add', id: url, actor: byAccount.url, to, + cc, object, target } diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts index 0ea522e5c..8e8a3961b 100644 --- a/server/middlewares/activitypub.ts +++ b/server/middlewares/activitypub.ts @@ -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 { isSignatureVerified, logger } from '../helpers' import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub' -import { database as db, ACTIVITY_PUB_ACCEPT_HEADER } from '../initializers' -import { each, eachSeries, waterfall } from 'async' +import { database as db } from '../initializers' +import { ACTIVITY_PUB } from '../initializers/constants' async function checkSignature (req: Request, res: Response, next: NextFunction) { const signatureObject: ActivityPubSignature = req.body.signature @@ -37,7 +38,7 @@ async function checkSignature (req: Request, res: Response, next: NextFunction) function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) { 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() } diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 5fb254b2d..dc10aca1a 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -550,6 +550,7 @@ toFormattedDetailsJSON = function (this: VideoInstance) { toActivityPubObject = function (this: VideoInstance) { const { baseUrlHttp, baseUrlWs } = getBaseUrls(this) + if (!this.Tags) this.Tags = [] const tag = this.Tags.map(t => ({ type: 'Hashtag' as 'Hashtag', diff --git a/server/tests/api/check-params/follows.ts b/server/tests/api/check-params/follows.ts new file mode 100644 index 000000000..d742200c1 --- /dev/null +++ b/server/tests/api/check-params/follows.ts @@ -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() + } + }) +}) diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts index 954b206e9..287480808 100644 --- a/server/tests/api/check-params/index.ts +++ b/server/tests/api/check-params/index.ts @@ -1,8 +1,6 @@ // Order of the tests we want to execute -import './pods' -import './remotes' +import './follows' import './users' -import './request-schedulers' import './services' import './videos' import './video-abuses' diff --git a/server/tests/api/check-params/pods.ts b/server/tests/api/check-params/pods.ts deleted file mode 100644 index 9f9c2e4f0..000000000 --- a/server/tests/api/check-params/pods.ts +++ /dev/null @@ -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() - } - }) -}) diff --git a/server/tests/api/check-params/remotes.ts b/server/tests/api/check-params/remotes.ts deleted file mode 100644 index 6d1747442..000000000 --- a/server/tests/api/check-params/remotes.ts +++ /dev/null @@ -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() - } - }) -}) diff --git a/server/tests/api/check-params/request-schedulers.ts b/server/tests/api/check-params/request-schedulers.ts deleted file mode 100644 index 01a54ffa1..000000000 --- a/server/tests/api/check-params/request-schedulers.ts +++ /dev/null @@ -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() - } - }) -}) diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts index 5860e9195..7f5609784 100644 --- a/server/tests/api/check-params/videos.ts +++ b/server/tests/api/check-params/videos.ts @@ -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 pod') + it('Should fail with a video of another server') it('Should succeed with the correct parameters', async function () { 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 pod') + it('Should fail with a video of another server') it('Should succeed with the correct parameters') }) diff --git a/server/tests/api/index-fast.ts b/server/tests/api/index-fast.ts index 590b0d51c..35b414383 100644 --- a/server/tests/api/index-fast.ts +++ b/server/tests/api/index-fast.ts @@ -2,7 +2,7 @@ import './config' import './check-params' import './users' -import './single-pod' +import './single-server' import './video-abuse' import './video-blacklist' import './video-blacklist-management' diff --git a/server/tests/api/index-slow.ts b/server/tests/api/index-slow.ts index a2faf8d0f..e554c25f8 100644 --- a/server/tests/api/index-slow.ts +++ b/server/tests/api/index-slow.ts @@ -1,3 +1,3 @@ // Order of the tests we want to execute -import './multiple-pods' +import './multiple-servers' import './video-transcoder' diff --git a/server/tests/api/multiple-pods.ts b/server/tests/api/multiple-servers.ts similarity index 80% rename from server/tests/api/multiple-pods.ts rename to server/tests/api/multiple-servers.ts index 3c6b3f650..737770992 100644 --- a/server/tests/api/multiple-pods.ts +++ b/server/tests/api/multiple-servers.ts @@ -10,7 +10,6 @@ import { getVideo, getVideosList, killallServers, - makeFriends, rateVideo, removeVideo, ServerInfo, @@ -22,13 +21,14 @@ import { webtorrentAdd, addVideoChannel, getVideoChannelsList, - getUserAccessToken + getUserAccessToken, + doubleFollow } from '../utils' import { createUser } from '../utils/users' const expect = chai.expect -describe('Test multiple pods', function () { +describe('Test multiple servers', function () { let servers: ServerInfo[] = [] const toRemove = [] let videoUUID = '' @@ -50,17 +50,15 @@ describe('Test multiple pods', function () { const channelRes = await getVideoChannelsList(servers[0].url, 0, 1) videoChannelId = channelRes.body.data[0].id - // The second pod make friend with the third - await makeFriends(servers[1].url, servers[1].accessToken) - - // Wait for the request between pods - await wait(10000) - - // Pod 1 make friends too - await makeFriends(servers[0].url, servers[0].accessToken) + // 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 pods', async function () { + 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 @@ -69,18 +67,18 @@ describe('Test multiple pods', function () { } }) - describe('Should upload the video and propagate on each pod', function () { - it('Should upload the video on pod 1 and propagate on each pod', async function () { - // Pod 1 has video transcoding activated + 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 () { + // Server 1 has video transcoding activated this.timeout(15000) const videoAttributes = { - name: 'my super name for pod 1', + name: 'my super name for server 1', category: 5, licence: 4, language: 9, nsfw: true, - description: 'my super description for pod 1', + description: 'my super description for server 1', tags: [ 'tag1p1', 'tag2p1' ], channelId: videoChannelId, fixture: 'video_short1.webm' @@ -89,7 +87,7 @@ describe('Test multiple pods', function () { await wait(11000) - // All pods should have this video + // All servers should have this video for (const server of servers) { let baseMagnet = null @@ -99,7 +97,7 @@ describe('Test multiple pods', function () { 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 pod 1') + 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) @@ -107,8 +105,8 @@ describe('Test multiple pods', function () { 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 pod 1') - expect(video.podHost).to.equal('localhost:9001') + 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(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ]) expect(dateIsValid(video.createdAt)).to.be.true @@ -127,8 +125,9 @@ describe('Test multiple pods', function () { const file = videoDetails.files[0] const magnetUri = file.magnetUri 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.fileUrl).to.equal(`http://${videoDetails.podHost}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`) + 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) @@ -141,7 +140,7 @@ describe('Test multiple pods', function () { 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) { baseMagnet = magnetUri } 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) const user = { @@ -164,12 +163,12 @@ describe('Test multiple pods', function () { const userAccessToken = await getUserAccessToken(servers[1], user) const videoAttributes = { - name: 'my super name for pod 2', + name: 'my super name for server 2', category: 4, licence: 3, language: 11, nsfw: true, - description: 'my super description for pod 2', + description: 'my super description for server 2', tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], fixture: 'video_short2.webm' } @@ -178,7 +177,7 @@ describe('Test multiple pods', function () { // Transcoding, so wait more than 22000 await wait(60000) - // All pods should have this video + // All servers should have this video for (const server of servers) { let baseMagnet = {} @@ -188,7 +187,7 @@ describe('Test multiple pods', function () { 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 pod 2') + 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) @@ -196,8 +195,8 @@ describe('Test multiple pods', function () { 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 pod 2') - expect(video.podHost).to.equal('localhost:9002') + 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(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ]) expect(dateIsValid(video.createdAt)).to.be.true @@ -223,7 +222,7 @@ describe('Test multiple pods', function () { for (const file of videoDetails.files) { 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) { baseMagnet[file.resolution] = file.magnet } 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) const videoAttributes1 = { - name: 'my super name for pod 3', + name: 'my super name for server 3', category: 6, licence: 5, language: 11, nsfw: true, - description: 'my super description for pod 3', + 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 pod 3-2', + name: 'my super name for server 3-2', category: 7, licence: 6, language: 12, nsfw: false, - description: 'my super description for pod 3-2', + description: 'my super description for server 3-2', tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], fixture: 'video_short.webm' } @@ -286,7 +285,7 @@ describe('Test multiple pods', function () { await wait(33000) let baseMagnet = null - // All pods should have this video + // All servers should have this video for (const server of servers) { 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 let video1 = 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] video2 = videos[3] } else { @@ -305,7 +304,7 @@ describe('Test multiple pods', function () { 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.categoryLabel).to.equal('Travels') expect(video1.licence).to.equal(5) @@ -313,8 +312,8 @@ describe('Test multiple pods', function () { 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 pod 3') - expect(video1.podHost).to.equal('localhost:9003') + 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.tags).to.deep.equal([ 'tag1p3' ]) expect(video1.author).to.equal('root') @@ -331,7 +330,7 @@ describe('Test multiple pods', function () { expect(file1.resolutionLabel).to.equal('720p') 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.categoryLabel).to.equal('Gaming') expect(video2.licence).to.equal(6) @@ -339,8 +338,8 @@ describe('Test multiple pods', function () { 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 pod 3-2') - expect(video2.podHost).to.equal('localhost:9003') + 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.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ]) expect(video2.author).to.equal('root') @@ -367,7 +366,7 @@ describe('Test multiple pods', function () { 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) { baseMagnet = magnetUri2 } else { @@ -384,7 +383,7 @@ describe('Test multiple pods', 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 this.timeout(200000) @@ -403,7 +402,7 @@ describe('Test multiple pods', function () { 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 this.timeout(200000) @@ -419,7 +418,7 @@ describe('Test multiple pods', function () { 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 this.timeout(200000) @@ -435,7 +434,7 @@ describe('Test multiple pods', function () { 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 this.timeout(200000) @@ -451,13 +450,13 @@ describe('Test multiple pods', function () { 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 this.timeout(200000) 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 videoDetails = res2.body @@ -472,31 +471,31 @@ describe('Test multiple pods', function () { }) describe('Should update video views, likes and dislikes', function () { - let localVideosPod3 = [] - let remoteVideosPod1 = [] - let remoteVideosPod2 = [] - let remoteVideosPod3 = [] + let localVideosServer3 = [] + let remoteVideosServer1 = [] + let remoteVideosServer2 = [] + let remoteVideosServer3 = [] before(async function () { 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) - 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) - localVideosPod3 = 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) + 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(30000) const tasks: Promise[] = [] - tasks.push(getVideo(servers[2].url, localVideosPod3[0])) - tasks.push(getVideo(servers[2].url, localVideosPod3[0])) - tasks.push(getVideo(servers[2].url, localVideosPod3[0])) - tasks.push(getVideo(servers[2].url, localVideosPod3[1])) + tasks.push(getVideo(servers[2].url, localVideosServer3[0])) + tasks.push(getVideo(servers[2].url, localVideosServer3[0])) + tasks.push(getVideo(servers[2].url, localVideosServer3[0])) + tasks.push(getVideo(servers[2].url, localVideosServer3[1])) await Promise.all(tasks) @@ -506,8 +505,8 @@ describe('Test multiple pods', function () { const res = await getVideosList(server.url) const videos = res.body.data - const video0 = videos.find(v => v.uuid === localVideosPod3[0]) - const video1 = videos.find(v => v.uuid === localVideosPod3[1]) + const video0 = videos.find(v => v.uuid === localVideosServer3[0]) + const video1 = videos.find(v => v.uuid === localVideosServer3[1]) expect(video0.views).to.equal(7) expect(video1.views).to.equal(5) @@ -518,16 +517,16 @@ describe('Test multiple pods', function () { this.timeout(30000) const tasks: Promise[] = [] - tasks.push(getVideo(servers[0].url, remoteVideosPod1[0])) - tasks.push(getVideo(servers[1].url, remoteVideosPod2[0])) - tasks.push(getVideo(servers[1].url, remoteVideosPod2[0])) - tasks.push(getVideo(servers[2].url, remoteVideosPod3[0])) - tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) - tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) - tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) - tasks.push(getVideo(servers[2].url, localVideosPod3[1])) - tasks.push(getVideo(servers[2].url, localVideosPod3[1])) - tasks.push(getVideo(servers[2].url, localVideosPod3[1])) + tasks.push(getVideo(servers[0].url, remoteVideosServer1[0])) + tasks.push(getVideo(servers[1].url, remoteVideosServer2[0])) + tasks.push(getVideo(servers[1].url, remoteVideosServer2[0])) + tasks.push(getVideo(servers[2].url, remoteVideosServer3[0])) + tasks.push(getVideo(servers[2].url, remoteVideosServer3[1])) + tasks.push(getVideo(servers[2].url, remoteVideosServer3[1])) + tasks.push(getVideo(servers[2].url, remoteVideosServer3[1])) + tasks.push(getVideo(servers[2].url, localVideosServer3[1])) + tasks.push(getVideo(servers[2].url, localVideosServer3[1])) + tasks.push(getVideo(servers[2].url, localVideosServer3[1])) await Promise.all(tasks) @@ -557,13 +556,13 @@ describe('Test multiple pods', function () { this.timeout(30000) const tasks: Promise[] = [] - tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like')) - tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'dislike')) - tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like')) - tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'like')) - tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'dislike')) - tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[1], 'dislike')) - tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[0], 'like')) + 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) @@ -591,7 +590,7 @@ describe('Test multiple pods', 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) const attributes = { @@ -609,7 +608,7 @@ describe('Test multiple pods', function () { 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) 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) await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id) @@ -660,7 +659,7 @@ describe('Test multiple pods', function () { 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) { 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[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 for (const server of servers) { 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) { const res = await getVideo(server.url, videoUUID) const video = res.body diff --git a/server/tests/api/services.ts b/server/tests/api/services.ts index c34c51f66..3e6506de4 100644 --- a/server/tests/api/services.ts +++ b/server/tests/api/services.ts @@ -14,7 +14,6 @@ import { getOEmbed } from '../utils' import { runServer } from '../utils/servers' -import { Video } from '../../../client/src/app/videos/shared/video.model' describe('Test services', function () { let server: ServerInfo = null diff --git a/server/tests/api/single-pod.ts b/server/tests/api/single-server.ts similarity index 99% rename from server/tests/api/single-pod.ts rename to server/tests/api/single-server.ts index 0a917f2ae..82ecc68ee 100644 --- a/server/tests/api/single-pod.ts +++ b/server/tests/api/single-server.ts @@ -34,7 +34,7 @@ import { updateVideo } from '../utils' -describe('Test a single pod', function () { +describe('Test a single server', function () { let server: ServerInfo = null let videoId = -1 let videoUUID = '' diff --git a/server/tests/api/video-privacy.ts b/server/tests/api/video-privacy.ts index eb1e4f873..bf91ead2e 100644 --- a/server/tests/api/video-privacy.ts +++ b/server/tests/api/video-privacy.ts @@ -40,14 +40,14 @@ describe('Test video privacy', function () { }) it('Should upload a private video on server 1', async function () { - this.timeout(15000) + this.timeout(25000) const attributes = { privacy: VideoPrivacy.PRIVATE } 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 () { @@ -86,8 +86,8 @@ describe('Test video privacy', function () { await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID) }) - it('Should upload a unlisted video on server 2', async function () { - this.timeout(30000) + it('Should upload an unlisted video on server 2', async function () { + this.timeout(50000) const attributes = { name: 'unlisted video', @@ -95,7 +95,7 @@ describe('Test video privacy', function () { } 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 () { @@ -125,7 +125,7 @@ describe('Test video privacy', function () { }) it('Should update the private video to public on server 1', async function () { - this.timeout(15000) + this.timeout(40000) const attribute = { name: 'super video public', @@ -134,10 +134,10 @@ describe('Test video privacy', function () { 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) { const res = await getVideosList(server.url) diff --git a/server/tests/client.ts b/server/tests/client.ts index 5f947ed2b..0d70e3451 100644 --- a/server/tests/client.ts +++ b/server/tests/client.ts @@ -26,8 +26,8 @@ describe('Test a client controllers', function () { server.accessToken = await loginAndGetAccessToken(server) const videoAttributes = { - name: 'my super name for pod 1', - description: 'my super description for pod 1' + name: 'my super name for server 1', + description: 'my super description for server 1' } await uploadVideo(server.url, server.accessToken, videoAttributes) @@ -44,8 +44,8 @@ describe('Test a client controllers', function () { .get('/videos/watch/' + server.video.id) .expect(200) - expect(res.text).to.contain('') - expect(res.text).to.contain('') + expect(res.text).to.contain('') + expect(res.text).to.contain('') }) 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) .expect(200) - expect(res.text).to.contain('') - expect(res.text).to.contain('') + expect(res.text).to.contain('') + expect(res.text).to.contain('') }) it('Should have valid oEmbed discovery tags', async function () { diff --git a/server/tests/real-world/real-world.ts b/server/tests/real-world/real-world.ts index c79ad38ff..ac83d64a6 100644 --- a/server/tests/real-world/real-world.ts +++ b/server/tests/real-world/real-world.ts @@ -1,25 +1,24 @@ -import * as program from 'commander' - // /!\ Before imports /!\ process.env.NODE_ENV = 'test' -import { Video, VideoRateType, VideoFile } from '../../../shared' +import * as program from 'commander' +import { Video, VideoFile, VideoRateType } from '../../../shared' import { - ServerInfo as DefaultServerInfo, flushAndRunMultipleServers, - setAccessTokensToServers, - makeFriends, - wait, - killallServers, flushTests, - uploadVideo, - getVideosList, - updateVideo, - removeVideo, - getVideo, getAllVideosListBy, - getRequestsStats + getRequestsStats, + getVideo, + getVideosList, + killallServers, + removeVideo, + ServerInfo as DefaultServerInfo, + setAccessTokensToServers, + updateVideo, + uploadVideo, + wait } from '../utils' +import { follow } from '../utils/follows' interface ServerInfo extends DefaultServerInfo { requestsNumber: number @@ -32,7 +31,7 @@ program .option('-v, --view [weight]', 'Weight for viewing videos') .option('-l, --like [weight]', 'Weight for liking 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-integrity [interval]', 'Interval in ms for an integrity check') .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 displayDiffOnFail = program['difference'] || false -const numberOfPods = 6 +const numberOfServers = 6 console.log( '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 () { - const servers = await runServers(numberOfPods) + const servers = await runServers(numberOfServers) process.on('exit', async () => { await exitServers(servers, flushAtExit) @@ -152,22 +151,20 @@ function getRandomNumServer (servers) { return getRandomInt(0, servers.length) } -async function runServers (numberOfPods: number) { - const servers: ServerInfo[] = (await flushAndRunMultipleServers(numberOfPods)) +async function runServers (numberOfServers: number) { + const servers: ServerInfo[] = (await flushAndRunMultipleServers(numberOfServers)) .map(s => Object.assign({ requestsNumber: 0 }, s)) // Get the access tokens await setAccessTokensToServers(servers) - await makeFriends(servers[1].url, servers[1].accessToken) - await makeFriends(servers[0].url, servers[0].accessToken) - await wait(1000) + for (let i = 0; i < numberOfServers; i++) { + for (let j = 0; j < numberOfServers; j++) { + if (i === j) continue - await makeFriends(servers[3].url, servers[3].accessToken) - await makeFriends(servers[5].url, servers[5].accessToken) - await makeFriends(servers[4].url, servers[4].accessToken) - - await wait(1000) + await follow(servers[i].url, [ servers[j].url ], servers[i].accessToken) + } + } return servers } @@ -259,7 +256,7 @@ async function checkIntegrity (servers: ServerInfo[]) { const videos: Video[][] = [] const tasks: Promise[] = [] - // 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) { const p = getAllVideosListBy(server.url).then(res => videos.push(res.body.data)) tasks.push(p) diff --git a/server/tests/utils/follows.ts b/server/tests/utils/follows.ts index b454fe2f8..cffc1648f 100644 --- a/server/tests/utils/follows.ts +++ b/server/tests/utils/follows.ts @@ -29,7 +29,7 @@ function getFollowingListPaginationAndSort (url: string, start: number, count: n } 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 res = await request(follower) diff --git a/shared/models/activitypub/activity.ts b/shared/models/activitypub/activity.ts index 6a05a1c39..2e6feeb16 100644 --- a/shared/models/activitypub/activity.ts +++ b/shared/models/activitypub/activity.ts @@ -12,6 +12,7 @@ export interface BaseActivity { '@context'?: any[] id: string to?: string[] + cc?: string[] actor: string type: ActivityType signature?: ActivityPubSignature