aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-11-17 15:20:42 +0100
committerChocobozzz <florian.bigard@gmail.com>2017-11-27 19:40:52 +0100
commit9a27cdc27c900feaae5f6db4315c4ccdfc0c4493 (patch)
treef91fcfa0fa1a2e45aae1c5333ef2f7ec60e56ef0
parent975e6e0e44e2f2b25f804cd48a62e2a8d9e8117a (diff)
downloadPeerTube-9a27cdc27c900feaae5f6db4315c4ccdfc0c4493.tar.gz
PeerTube-9a27cdc27c900feaae5f6db4315c4ccdfc0c4493.tar.zst
PeerTube-9a27cdc27c900feaae5f6db4315c4ccdfc0c4493.zip
Optimize signature verification
-rw-r--r--client/src/app/+admin/follows/shared/follow.service.ts2
-rw-r--r--server/controllers/api/server/follows.ts2
-rw-r--r--server/helpers/activitypub.ts6
-rw-r--r--server/helpers/custom-jsonld-signature.ts20
-rw-r--r--server/helpers/custom-validators/activitypub/misc.ts4
-rw-r--r--server/helpers/peertube-crypto.ts5
-rw-r--r--server/initializers/constants.ts4
-rw-r--r--server/lib/activitypub/misc.ts16
-rw-r--r--server/lib/activitypub/process-add.ts15
-rw-r--r--server/lib/activitypub/process-update.ts1
-rw-r--r--server/lib/activitypub/send-request.ts37
-rw-r--r--server/middlewares/activitypub.ts9
-rw-r--r--server/models/video/video.ts1
-rw-r--r--server/tests/api/check-params/follows.ts222
-rw-r--r--server/tests/api/check-params/index.ts4
-rw-r--r--server/tests/api/check-params/pods.ts287
-rw-r--r--server/tests/api/check-params/remotes.ts54
-rw-r--r--server/tests/api/check-params/request-schedulers.ts65
-rw-r--r--server/tests/api/check-params/videos.ts4
-rw-r--r--server/tests/api/index-fast.ts2
-rw-r--r--server/tests/api/index-slow.ts2
-rw-r--r--server/tests/api/multiple-servers.ts (renamed from server/tests/api/multiple-pods.ts)179
-rw-r--r--server/tests/api/services.ts1
-rw-r--r--server/tests/api/single-server.ts (renamed from server/tests/api/single-pod.ts)2
-rw-r--r--server/tests/api/video-privacy.ts16
-rw-r--r--server/tests/client.ts12
-rw-r--r--server/tests/real-world/real-world.ts51
-rw-r--r--server/tests/utils/follows.ts2
-rw-r--r--shared/models/activitypub/activity.ts1
29 files changed, 443 insertions, 583 deletions
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 {
42 hosts: notEmptyHosts 42 hosts: notEmptyHosts
43 } 43 }
44 44
45 return this.authHttp.post(FollowService.BASE_APPLICATION_URL + '/follow', body) 45 return this.authHttp.post(FollowService.BASE_APPLICATION_URL + '/following', body)
46 .map(this.restExtractor.extractDataBool) 46 .map(this.restExtractor.extractDataBool)
47 .catch(res => this.restExtractor.handleError(res)) 47 .catch(res => this.restExtractor.handleError(res))
48 } 48 }
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',
25 asyncMiddleware(listFollowing) 25 asyncMiddleware(listFollowing)
26) 26)
27 27
28serverFollowsRouter.post('/follow', 28serverFollowsRouter.post('/following',
29 authenticate, 29 authenticate,
30 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW), 30 ensureUserHasRight(UserRight.MANAGE_SERVER_FOLLOW),
31 followValidator, 31 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
8import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object' 8import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object'
9import { ResultList } from '../../shared/models/result-list.model' 9import { ResultList } from '../../shared/models/result-list.model'
10import { database as db, REMOTE_SCHEME } from '../initializers' 10import { database as db, REMOTE_SCHEME } from '../initializers'
11import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants' 11import { ACTIVITY_PUB, CONFIG, STATIC_PATHS } from '../initializers/constants'
12import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc' 12import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc'
13import { sendVideoAnnounce } from '../lib/activitypub/send-request' 13import { sendVideoAnnounce } from '../lib/activitypub/send-request'
14import { sendVideoChannelAnnounce } from '../lib/index' 14import { sendVideoChannelAnnounce } from '../lib/index'
@@ -99,7 +99,7 @@ async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
99 uri: accountUrl, 99 uri: accountUrl,
100 method: 'GET', 100 method: 'GET',
101 headers: { 101 headers: {
102 'Accept': ACTIVITY_PUB_ACCEPT_HEADER 102 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
103 } 103 }
104 } 104 }
105 105
@@ -157,7 +157,7 @@ async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChan
157 uri: videoChannelUrl, 157 uri: videoChannelUrl,
158 method: 'GET', 158 method: 'GET',
159 headers: { 159 headers: {
160 'Accept': ACTIVITY_PUB_ACCEPT_HEADER 160 'Accept': ACTIVITY_PUB.ACCEPT_HEADER
161 } 161 }
162 } 162 }
163 163
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 @@
1import * as AsyncLRU from 'async-lru'
2import * as jsonld from 'jsonld'
3import * as jsig from 'jsonld-signatures'
4
5jsig.use('jsonld', jsonld)
6
7const nodeDocumentLoader = jsonld.documentLoaders.node()
8
9const lru = new AsyncLRU({
10 max: 10,
11 load: (key, cb) => {
12 nodeDocumentLoader(key, cb)
13 }
14})
15
16jsonld.documentLoader = (url, cb) => {
17 lru.get(url, cb)
18}
19
20export { 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) {
28 ( 28 (
29 activity.to === undefined || 29 activity.to === undefined ||
30 (Array.isArray(activity.to) && activity.to.every(t => isActivityPubUrlValid(t))) 30 (Array.isArray(activity.to) && activity.to.every(t => isActivityPubUrlValid(t)))
31 ) &&
32 (
33 activity.cc === undefined ||
34 (Array.isArray(activity.cc) && activity.cc.every(t => isActivityPubUrlValid(t)))
31 ) 35 )
32} 36}
33 37
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 @@
1import * as jsonld from 'jsonld'
2import * as jsig from 'jsonld-signatures'
3jsig.use('jsonld', jsonld)
4
5import { 1import {
6 PRIVATE_RSA_KEY_SIZE, 2 PRIVATE_RSA_KEY_SIZE,
7 BCRYPT_SALT_SIZE 3 BCRYPT_SALT_SIZE
@@ -15,6 +11,7 @@ import {
15} from './core-utils' 11} from './core-utils'
16import { logger } from './logger' 12import { logger } from './logger'
17import { AccountInstance } from '../models/account/account-interface' 13import { AccountInstance } from '../models/account/account-interface'
14import { jsig } from './custom-jsonld-signature'
18 15
19async function createPrivateAndPublicKeys () { 16async function createPrivateAndPublicKeys () {
20 logger.info('Generating a RSA key...') 17 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 = {
223} 223}
224 224
225const SERVER_ACCOUNT_NAME = 'peertube' 225const SERVER_ACCOUNT_NAME = 'peertube'
226const ACTIVITY_PUB_ACCEPT_HEADER = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
227 226
228const ACTIVITY_PUB = { 227const ACTIVITY_PUB = {
228 ACCEPT_HEADER: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
229 PUBLIC: 'https://www.w3.org/ns/activitystreams#Public',
229 COLLECTION_ITEMS_PER_PAGE: 10, 230 COLLECTION_ITEMS_PER_PAGE: 10,
230 URL_MIME_TYPES: { 231 URL_MIME_TYPES: {
231 VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT 232 VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT
@@ -349,7 +350,6 @@ export {
349 SERVERS_SCORE, 350 SERVERS_SCORE,
350 PREVIEWS_SIZE, 351 PREVIEWS_SIZE,
351 REMOTE_SCHEME, 352 REMOTE_SCHEME,
352 ACTIVITY_PUB_ACCEPT_HEADER,
353 FOLLOW_STATES, 353 FOLLOW_STATES,
354 SEARCHABLE_COLUMNS, 354 SEARCHABLE_COLUMNS,
355 SERVER_ACCOUNT_NAME, 355 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'
2import { VideoTorrentObject } from '../../../shared' 2import { VideoTorrentObject } from '../../../shared'
3import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' 3import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
4import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 4import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
5import { VIDEO_MIMETYPE_EXT } from '../../initializers/constants' 5import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../initializers/constants'
6import { AccountInstance } from '../../models/account/account-interface' 6import { AccountInstance } from '../../models/account/account-interface'
7import { VideoChannelInstance } from '../../models/video/video-channel-interface' 7import { VideoChannelInstance } from '../../models/video/video-channel-interface'
8import { VideoFileAttributes } from '../../models/video/video-file-interface' 8import { VideoFileAttributes } from '../../models/video/video-file-interface'
9import { VideoAttributes, VideoInstance } from '../../models/video/video-interface' 9import { VideoAttributes, VideoInstance } from '../../models/video/video-interface'
10import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
10 11
11function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) { 12function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
12 return { 13 return {
@@ -23,8 +24,14 @@ function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChan
23 24
24async function videoActivityObjectToDBAttributes ( 25async function videoActivityObjectToDBAttributes (
25 videoChannel: VideoChannelInstance, 26 videoChannel: VideoChannelInstance,
26 videoObject: VideoTorrentObject 27 videoObject: VideoTorrentObject,
28 to: string[] = [],
29 cc: string[] = []
27) { 30) {
31 let privacy = VideoPrivacy.PRIVATE
32 if (to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.PUBLIC
33 else if (cc.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.UNLISTED
34
28 const duration = videoObject.duration.replace(/[^\d]+/, '') 35 const duration = videoObject.duration.replace(/[^\d]+/, '')
29 const videoData: VideoAttributes = { 36 const videoData: VideoAttributes = {
30 name: videoObject.name, 37 name: videoObject.name,
@@ -43,11 +50,8 @@ async function videoActivityObjectToDBAttributes (
43 views: videoObject.views, 50 views: videoObject.views,
44 likes: 0, 51 likes: 0,
45 dislikes: 0, 52 dislikes: 0,
46 // likes: videoToCreateData.likes,
47 // dislikes: videoToCreateData.dislikes,
48 remote: true, 53 remote: true,
49 privacy: 1 54 privacy
50 // privacy: videoToCreateData.privacy
51 } 55 }
52 56
53 return videoData 57 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) {
17 const videoChannelUrl = activity.target 17 const videoChannelUrl = activity.target
18 const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl) 18 const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl)
19 19
20 return processAddVideo(account, videoChannel, activityObject as VideoTorrentObject) 20 return processAddVideo(account, activity, videoChannel, activityObject as VideoTorrentObject)
21 } 21 }
22 22
23 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) 23 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
@@ -32,16 +32,21 @@ export {
32 32
33// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
34 34
35function processAddVideo (account: AccountInstance, videoChannel: VideoChannelInstance, video: VideoTorrentObject) { 35function processAddVideo (account: AccountInstance, activity: ActivityAdd, videoChannel: VideoChannelInstance, video: VideoTorrentObject) {
36 const options = { 36 const options = {
37 arguments: [ account, videoChannel, video ], 37 arguments: [ account, activity, videoChannel, video ],
38 errorMessage: 'Cannot insert the remote video with many retries.' 38 errorMessage: 'Cannot insert the remote video with many retries.'
39 } 39 }
40 40
41 return retryTransactionWrapper(addRemoteVideo, options) 41 return retryTransactionWrapper(addRemoteVideo, options)
42} 42}
43 43
44function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelInstance, videoToCreateData: VideoTorrentObject) { 44function addRemoteVideo (
45 account: AccountInstance,
46 activity: ActivityAdd,
47 videoChannel: VideoChannelInstance,
48 videoToCreateData: VideoTorrentObject
49) {
45 logger.debug('Adding remote video %s.', videoToCreateData.url) 50 logger.debug('Adding remote video %s.', videoToCreateData.url)
46 51
47 return db.sequelize.transaction(async t => { 52 return db.sequelize.transaction(async t => {
@@ -54,7 +59,7 @@ function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelIns
54 const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t) 59 const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
55 if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.') 60 if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.')
56 61
57 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData) 62 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc)
58 const video = db.Video.build(videoData) 63 const video = db.Video.build(videoData)
59 64
60 // Don't block on request 65 // 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
70 videoInstance.set('views', videoData.views) 70 videoInstance.set('views', videoData.views)
71 // videoInstance.set('likes', videoData.likes) 71 // videoInstance.set('likes', videoData.likes)
72 // videoInstance.set('dislikes', videoData.dislikes) 72 // videoInstance.set('dislikes', videoData.dislikes)
73 // videoInstance.set('privacy', videoData.privacy)
74 73
75 await videoInstance.save(sequelizeOptions) 74 await videoInstance.save(sequelizeOptions)
76 75
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'
13import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../models' 13import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../models'
14import { VideoAbuseInstance } from '../../models/video/video-abuse-interface' 14import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
15import { activitypubHttpJobScheduler } from '../jobs' 15import { activitypubHttpJobScheduler } from '../jobs'
16import { ACTIVITY_PUB } from '../../initializers/constants'
17import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
16 18
17async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { 19async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
18 const byAccount = videoChannel.Account 20 const byAccount = videoChannel.Account
@@ -50,7 +52,7 @@ async function sendAddVideo (video: VideoInstance, t: Transaction) {
50 const byAccount = video.VideoChannel.Account 52 const byAccount = video.VideoChannel.Account
51 53
52 const videoObject = video.toActivityPubObject() 54 const videoObject = video.toActivityPubObject()
53 const data = await addActivityData(video.url, byAccount, video.VideoChannel.url, videoObject) 55 const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject)
54 56
55 return broadcastToFollowers(data, byAccount, [ byAccount ], t) 57 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
56} 58}
@@ -96,7 +98,7 @@ async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstan
96 const url = getActivityPubUrl('video', video.uuid) + '#announce' 98 const url = getActivityPubUrl('video', video.uuid) + '#announce'
97 99
98 const videoChannel = video.VideoChannel 100 const videoChannel = video.VideoChannel
99 const announcedActivity = await addActivityData(url, videoChannel.Account, videoChannel.url, video.toActivityPubObject()) 101 const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
100 102
101 const data = await announceActivityData(url, byAccount, announcedActivity) 103 const data = await announceActivityData(url, byAccount, announcedActivity)
102 return broadcastToFollowers(data, byAccount, [ byAccount ], t) 104 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
@@ -167,19 +169,32 @@ async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: s
167 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload) 169 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
168} 170}
169 171
170async function getPublicActivityTo (account: AccountInstance) { 172async function getAudience (accountSender: AccountInstance, isPublic = true) {
171 const inboxUrls = await account.getFollowerSharedInboxUrls() 173 const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
172 174
173 return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public') 175 // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
176 let to = []
177 let cc = []
178
179 if (isPublic) {
180 to = [ ACTIVITY_PUB.PUBLIC ]
181 cc = followerInboxUrls
182 } else { // Unlisted
183 to = followerInboxUrls
184 cc = [ ACTIVITY_PUB.PUBLIC ]
185 }
186
187 return { to, cc }
174} 188}
175 189
176async function createActivityData (url: string, byAccount: AccountInstance, object: any) { 190async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
177 const to = await getPublicActivityTo(byAccount) 191 const { to, cc } = await getAudience(byAccount)
178 const activity: ActivityCreate = { 192 const activity: ActivityCreate = {
179 type: 'Create', 193 type: 'Create',
180 id: url, 194 id: url,
181 actor: byAccount.url, 195 actor: byAccount.url,
182 to, 196 to,
197 cc,
183 object 198 object
184 } 199 }
185 200
@@ -187,12 +202,13 @@ async function createActivityData (url: string, byAccount: AccountInstance, obje
187} 202}
188 203
189async function updateActivityData (url: string, byAccount: AccountInstance, object: any) { 204async function updateActivityData (url: string, byAccount: AccountInstance, object: any) {
190 const to = await getPublicActivityTo(byAccount) 205 const { to, cc } = await getAudience(byAccount)
191 const activity: ActivityUpdate = { 206 const activity: ActivityUpdate = {
192 type: 'Update', 207 type: 'Update',
193 id: url, 208 id: url,
194 actor: byAccount.url, 209 actor: byAccount.url,
195 to, 210 to,
211 cc,
196 object 212 object
197 } 213 }
198 214
@@ -209,13 +225,16 @@ async function deleteActivityData (url: string, byAccount: AccountInstance) {
209 return activity 225 return activity
210} 226}
211 227
212async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any) { 228async function addActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, target: string, object: any) {
213 const to = await getPublicActivityTo(byAccount) 229 const videoPublic = video.privacy === VideoPrivacy.PUBLIC
230
231 const { to, cc } = await getAudience(byAccount, videoPublic)
214 const activity: ActivityAdd = { 232 const activity: ActivityAdd = {
215 type: 'Add', 233 type: 'Add',
216 id: url, 234 id: url,
217 actor: byAccount.url, 235 actor: byAccount.url,
218 to, 236 to,
237 cc,
219 object, 238 object,
220 target 239 target
221 } 240 }
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 @@
1import { NextFunction, Request, Response, RequestHandler } from 'express' 1import { eachSeries } from 'async'
2import { NextFunction, Request, RequestHandler, Response } from 'express'
2import { ActivityPubSignature } from '../../shared' 3import { ActivityPubSignature } from '../../shared'
3import { isSignatureVerified, logger } from '../helpers' 4import { isSignatureVerified, logger } from '../helpers'
4import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub' 5import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub'
5import { database as db, ACTIVITY_PUB_ACCEPT_HEADER } from '../initializers' 6import { database as db } from '../initializers'
6import { each, eachSeries, waterfall } from 'async' 7import { ACTIVITY_PUB } from '../initializers/constants'
7 8
8async function checkSignature (req: Request, res: Response, next: NextFunction) { 9async function checkSignature (req: Request, res: Response, next: NextFunction) {
9 const signatureObject: ActivityPubSignature = req.body.signature 10 const signatureObject: ActivityPubSignature = req.body.signature
@@ -37,7 +38,7 @@ async function checkSignature (req: Request, res: Response, next: NextFunction)
37 38
38function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) { 39function executeIfActivityPub (fun: RequestHandler | RequestHandler[]) {
39 return (req: Request, res: Response, next: NextFunction) => { 40 return (req: Request, res: Response, next: NextFunction) => {
40 if (req.header('Accept') !== ACTIVITY_PUB_ACCEPT_HEADER) { 41 if (req.header('Accept') !== ACTIVITY_PUB.ACCEPT_HEADER) {
41 return next() 42 return next()
42 } 43 }
43 44
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) {
550 550
551toActivityPubObject = function (this: VideoInstance) { 551toActivityPubObject = function (this: VideoInstance) {
552 const { baseUrlHttp, baseUrlWs } = getBaseUrls(this) 552 const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
553 if (!this.Tags) this.Tags = []
553 554
554 const tag = this.Tags.map(t => ({ 555 const tag = this.Tags.map(t => ({
555 type: 'Hashtag' as 'Hashtag', 556 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 @@
1/* tslint:disable:no-unused-expression */
2
3import * as request from 'supertest'
4import 'mocha'
5
6import {
7 ServerInfo,
8 flushTests,
9 runServer,
10 createUser,
11 loginAndGetAccessToken,
12 setAccessTokensToServers,
13 killallServers,
14 makePostBodyRequest
15} from '../../utils'
16
17describe('Test server follows API validators', function () {
18 let server: ServerInfo
19
20 // ---------------------------------------------------------------
21
22 before(async function () {
23 this.timeout(45000)
24
25 await flushTests()
26 server = await runServer(1)
27
28 await setAccessTokensToServers([ server ])
29 })
30
31 describe('When managing following', function () {
32 let userAccessToken = null
33
34 before(async function () {
35 await createUser(server.url, server.accessToken, 'user1', 'password')
36 server.user = {
37 username: 'user1',
38 password: 'password'
39 }
40
41 userAccessToken = await loginAndGetAccessToken(server)
42 })
43
44 describe('When adding follows', function () {
45 const path = '/api/v1/server/following'
46 const body = {
47 hosts: [ 'localhost:9002' ]
48 }
49
50 it('Should fail without hosts', async function () {
51 await request(server.url)
52 .post(path)
53 .set('Authorization', 'Bearer ' + server.accessToken)
54 .set('Accept', 'application/json')
55 .expect(400)
56 })
57
58 it('Should fail if hosts is not an array', async function () {
59 await request(server.url)
60 .post(path)
61 .send({ hosts: 'localhost:9002' })
62 .set('Authorization', 'Bearer ' + server.accessToken)
63 .set('Accept', 'application/json')
64 .expect(400)
65 })
66
67 it('Should fail if the array is not composed by hosts', async function () {
68 await request(server.url)
69 .post(path)
70 .send({ hosts: [ 'localhost:9002', 'localhost:coucou' ] })
71 .set('Authorization', 'Bearer ' + server.accessToken)
72 .set('Accept', 'application/json')
73 .expect(400)
74 })
75
76 it('Should fail if the array is composed with http schemes', async function () {
77 await request(server.url)
78 .post(path)
79 .send({ hosts: [ 'localhost:9002', 'http://localhost:9003' ] })
80 .set('Authorization', 'Bearer ' + server.accessToken)
81 .set('Accept', 'application/json')
82 .expect(400)
83 })
84
85 it('Should fail if hosts are not unique', async function () {
86 await request(server.url)
87 .post(path)
88 .send({ urls: [ 'localhost:9002', 'localhost:9002' ] })
89 .set('Authorization', 'Bearer ' + server.accessToken)
90 .set('Accept', 'application/json')
91 .expect(400)
92 })
93
94 it('Should fail with an invalid token', async function () {
95 await request(server.url)
96 .post(path)
97 .send(body)
98 .set('Authorization', 'Bearer fake_token')
99 .set('Accept', 'application/json')
100 .expect(401)
101 })
102
103 it('Should fail if the user is not an administrator', async function () {
104 await request(server.url)
105 .post(path)
106 .send(body)
107 .set('Authorization', 'Bearer ' + userAccessToken)
108 .set('Accept', 'application/json')
109 .expect(403)
110 })
111 })
112
113 describe('When listing followings', function () {
114 const path = '/api/v1/server/following'
115
116 it('Should fail with a bad start pagination', async function () {
117 await request(server.url)
118 .get(path)
119 .query({ start: 'hello' })
120 .set('Accept', 'application/json')
121 .expect(400)
122 })
123
124 it('Should fail with a bad count pagination', async function () {
125 await request(server.url)
126 .get(path)
127 .query({ count: 'hello' })
128 .set('Accept', 'application/json')
129 .expect(400)
130 })
131
132 it('Should fail with an incorrect sort', async function () {
133 await request(server.url)
134 .get(path)
135 .query({ sort: 'hello' })
136 .set('Accept', 'application/json')
137 .expect(400)
138 })
139 })
140
141 describe('When listing followers', function () {
142 const path = '/api/v1/server/followers'
143
144 it('Should fail with a bad start pagination', async function () {
145 await request(server.url)
146 .get(path)
147 .query({ start: 'hello' })
148 .set('Accept', 'application/json')
149 .expect(400)
150 })
151
152 it('Should fail with a bad count pagination', async function () {
153 await request(server.url)
154 .get(path)
155 .query({ count: 'hello' })
156 .set('Accept', 'application/json')
157 .expect(400)
158 })
159
160 it('Should fail with an incorrect sort', async function () {
161 await request(server.url)
162 .get(path)
163 .query({ sort: 'hello' })
164 .set('Accept', 'application/json')
165 .expect(400)
166 })
167 })
168
169 describe('When removing following', function () {
170 // it('Should fail with an invalid token', async function () {
171 // await request(server.url)
172 // .delete(path + '/1')
173 // .set('Authorization', 'Bearer faketoken')
174 // .set('Accept', 'application/json')
175 // .expect(401)
176 // })
177 //
178 // it('Should fail if the user is not an administrator', async function () {
179 // await request(server.url)
180 // .delete(path + '/1')
181 // .set('Authorization', 'Bearer ' + userAccessToken)
182 // .set('Accept', 'application/json')
183 // .expect(403)
184 // })
185 //
186 // it('Should fail with an undefined id', async function () {
187 // await request(server.url)
188 // .delete(path + '/' + undefined)
189 // .set('Authorization', 'Bearer ' + server.accessToken)
190 // .set('Accept', 'application/json')
191 // .expect(400)
192 // })
193 //
194 // it('Should fail with an invalid id', async function () {
195 // await request(server.url)
196 // .delete(path + '/foobar')
197 // .set('Authorization', 'Bearer ' + server.accessToken)
198 // .set('Accept', 'application/json')
199 // .expect(400)
200 // })
201 //
202 // it('Should fail we do not follow this server', async function () {
203 // await request(server.url)
204 // .delete(path + '/-1')
205 // .set('Authorization', 'Bearer ' + server.accessToken)
206 // .set('Accept', 'application/json')
207 // .expect(404)
208 // })
209 //
210 // it('Should succeed with the correct parameters')
211 })
212 })
213
214 after(async function () {
215 killallServers([ server ])
216
217 // Keep the logs if the test failed
218 if (this['ok']) {
219 await flushTests()
220 }
221 })
222})
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 @@
1// Order of the tests we want to execute 1// Order of the tests we want to execute
2import './pods' 2import './follows'
3import './remotes'
4import './users' 3import './users'
5import './request-schedulers'
6import './services' 4import './services'
7import './videos' 5import './videos'
8import './video-abuses' 6import './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 @@
1/* tslint:disable:no-unused-expression */
2
3import * as request from 'supertest'
4import 'mocha'
5
6import {
7 ServerInfo,
8 flushTests,
9 runServer,
10 createUser,
11 loginAndGetAccessToken,
12 setAccessTokensToServers,
13 killallServers,
14 makePostBodyRequest
15} from '../../utils'
16
17describe('Test pods API validators', function () {
18 let server: ServerInfo
19
20 // ---------------------------------------------------------------
21
22 before(async function () {
23 this.timeout(45000)
24
25 await flushTests()
26 server = await runServer(1)
27
28 await setAccessTokensToServers([ server ])
29 })
30
31 describe('When managing friends', function () {
32 const path = '/api/v1/pods/'
33 let userAccessToken = null
34
35 before(async function () {
36 await createUser(server.url, server.accessToken, 'user1', 'password')
37 server.user = {
38 username: 'user1',
39 password: 'password'
40 }
41
42 userAccessToken = await loginAndGetAccessToken(server)
43 })
44
45 describe('When making friends', function () {
46 const body = {
47 hosts: [ 'localhost:9002' ]
48 }
49
50 it('Should fail without hosts', async function () {
51 await request(server.url)
52 .post(path + '/make-friends')
53 .set('Authorization', 'Bearer ' + server.accessToken)
54 .set('Accept', 'application/json')
55 .expect(400)
56 })
57
58 it('Should fail if hosts is not an array', async function () {
59 await request(server.url)
60 .post(path + '/make-friends')
61 .send({ hosts: 'localhost:9002' })
62 .set('Authorization', 'Bearer ' + server.accessToken)
63 .set('Accept', 'application/json')
64 .expect(400)
65 })
66
67 it('Should fail if the array is not composed by hosts', async function () {
68 await request(server.url)
69 .post(path + '/make-friends')
70 .send({ hosts: [ 'localhost:9002', 'localhost:coucou' ] })
71 .set('Authorization', 'Bearer ' + server.accessToken)
72 .set('Accept', 'application/json')
73 .expect(400)
74 })
75
76 it('Should fail if the array is composed with http schemes', async function () {
77 await request(server.url)
78 .post(path + '/make-friends')
79 .send({ hosts: [ 'localhost:9002', 'http://localhost:9003' ] })
80 .set('Authorization', 'Bearer ' + server.accessToken)
81 .set('Accept', 'application/json')
82 .expect(400)
83 })
84
85 it('Should fail if hosts are not unique', async function () {
86 await request(server.url)
87 .post(path + '/make-friends')
88 .send({ urls: [ 'localhost:9002', 'localhost:9002' ] })
89 .set('Authorization', 'Bearer ' + server.accessToken)
90 .set('Accept', 'application/json')
91 .expect(400)
92 })
93
94 it('Should fail with an invalid token', async function () {
95 await request(server.url)
96 .post(path + '/make-friends')
97 .send(body)
98 .set('Authorization', 'Bearer fake_token')
99 .set('Accept', 'application/json')
100 .expect(401)
101 })
102
103 it('Should fail if the user is not an administrator', async function () {
104 await request(server.url)
105 .post(path + '/make-friends')
106 .send(body)
107 .set('Authorization', 'Bearer ' + userAccessToken)
108 .set('Accept', 'application/json')
109 .expect(403)
110 })
111 })
112
113 describe('When listing friends', function () {
114 it('Should fail with a bad start pagination', async function () {
115 await request(server.url)
116 .get(path)
117 .query({ start: 'hello' })
118 .set('Accept', 'application/json')
119 .expect(400)
120 })
121
122 it('Should fail with a bad count pagination', async function () {
123 await request(server.url)
124 .get(path)
125 .query({ count: 'hello' })
126 .set('Accept', 'application/json')
127 .expect(400)
128 })
129
130 it('Should fail with an incorrect sort', async function () {
131 await request(server.url)
132 .get(path)
133 .query({ sort: 'hello' })
134 .set('Accept', 'application/json')
135 .expect(400)
136 })
137 })
138
139 describe('When quitting friends', function () {
140 it('Should fail with an invalid token', async function () {
141 await request(server.url)
142 .get(path + '/quit-friends')
143 .query({ start: 'hello' })
144 .set('Authorization', 'Bearer faketoken')
145 .set('Accept', 'application/json')
146 .expect(401)
147 })
148
149 it('Should fail if the user is not an administrator', async function () {
150 await request(server.url)
151 .get(path + '/quit-friends')
152 .query({ start: 'hello' })
153 .set('Authorization', 'Bearer ' + userAccessToken)
154 .set('Accept', 'application/json')
155 .expect(403)
156 })
157 })
158
159 describe('When removing one friend', function () {
160 it('Should fail with an invalid token', async function () {
161 await request(server.url)
162 .delete(path + '/1')
163 .set('Authorization', 'Bearer faketoken')
164 .set('Accept', 'application/json')
165 .expect(401)
166 })
167
168 it('Should fail if the user is not an administrator', async function () {
169 await request(server.url)
170 .delete(path + '/1')
171 .set('Authorization', 'Bearer ' + userAccessToken)
172 .set('Accept', 'application/json')
173 .expect(403)
174 })
175
176 it('Should fail with an undefined id', async function () {
177 await request(server.url)
178 .delete(path + '/' + undefined)
179 .set('Authorization', 'Bearer ' + server.accessToken)
180 .set('Accept', 'application/json')
181 .expect(400)
182 })
183
184 it('Should fail with an invalid id', async function () {
185 await request(server.url)
186 .delete(path + '/foobar')
187 .set('Authorization', 'Bearer ' + server.accessToken)
188 .set('Accept', 'application/json')
189 .expect(400)
190 })
191
192 it('Should fail if the pod is not a friend', async function () {
193 await request(server.url)
194 .delete(path + '/-1')
195 .set('Authorization', 'Bearer ' + server.accessToken)
196 .set('Accept', 'application/json')
197 .expect(404)
198 })
199
200 it('Should succeed with the correct parameters')
201 })
202 })
203
204 describe('When adding a pod from remote', function () {
205 const path = '/api/v1/remote/pods/add'
206
207 it('Should fail with nothing', async function () {
208 const fields = {}
209 await makePostBodyRequest({ url: server.url, path, fields })
210 })
211
212 it('Should fail without public key', async function () {
213 const fields = {
214 email: 'test.example.com',
215 host: 'coucou.com'
216 }
217 await makePostBodyRequest({ url: server.url, path, fields })
218 })
219
220 it('Should fail without an email', async function () {
221 const fields = {
222 host: 'coucou.com',
223 publicKey: 'my super public key'
224 }
225 await makePostBodyRequest({ url: server.url, path, fields })
226 })
227
228 it('Should fail without an invalid email', async function () {
229 const fields = {
230 host: 'coucou.com',
231 email: 'test.example.com',
232 publicKey: 'my super public key'
233 }
234 await makePostBodyRequest({ url: server.url, path, fields })
235 })
236
237 it('Should fail without a host', async function () {
238 const fields = {
239 email: 'test.example.com',
240 publicKey: 'my super public key'
241 }
242 await makePostBodyRequest({ url: server.url, path, fields })
243 })
244
245 it('Should fail with an incorrect host', async function () {
246 const fields = {
247 host: 'http://coucou.com',
248 email: 'test.example.com',
249 publicKey: 'my super public key'
250 }
251 await makePostBodyRequest({ url: server.url, path, fields })
252
253 fields.host = 'http://coucou'
254 await makePostBodyRequest({ url: server.url, path, fields })
255
256 fields.host = 'coucou'
257 await makePostBodyRequest({ url: server.url, path, fields })
258 })
259
260 it('Should succeed with the correct parameters', async function () {
261 const fields = {
262 host: 'coucou.com',
263 email: 'test@example.com',
264 publicKey: 'my super public key'
265 }
266 await makePostBodyRequest({ url: server.url, path, fields, statusCodeExpected: 200 })
267 })
268
269 it('Should fail with a host that already exists', async function () {
270 const fields = {
271 host: 'coucou.com',
272 email: 'test@example.com',
273 publicKey: 'my super public key'
274 }
275 await makePostBodyRequest({ url: server.url, path, fields, statusCodeExpected: 409 })
276 })
277 })
278
279 after(async function () {
280 killallServers([ server ])
281
282 // Keep the logs if the test failed
283 if (this['ok']) {
284 await flushTests()
285 }
286 })
287})
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 @@
1/* tslint:disable:no-unused-expression */
2
3import 'mocha'
4
5import {
6 ServerInfo,
7 flushTests,
8 runServer,
9 setAccessTokensToServers,
10 killallServers
11} from '../../utils'
12
13describe('Test remote videos API validators', function () {
14 let server: ServerInfo
15
16 // ---------------------------------------------------------------
17
18 before(async function () {
19 this.timeout(60000)
20
21 await flushTests()
22
23 server = await runServer(1)
24
25 await setAccessTokensToServers([ server ])
26 })
27
28 describe('When making a secure request', async function () {
29 it('Should check a secure request')
30 })
31
32 describe('When adding a video', async function () {
33 it('Should check when adding a video')
34
35 it('Should not add an existing uuid')
36 })
37
38 describe('When removing a video', async function () {
39 it('Should check when removing a video')
40 })
41
42 describe('When reporting abuse on a video', async function () {
43 it('Should check when reporting a video abuse')
44 })
45
46 after(async function () {
47 killallServers([ server ])
48
49 // Keep the logs if the test failed
50 if (this['ok']) {
51 await flushTests()
52 }
53 })
54})
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 @@
1/* tslint:disable:no-unused-expression */
2
3import * as request from 'supertest'
4import 'mocha'
5
6import {
7 flushTests,
8 runServer,
9 createUser,
10 setAccessTokensToServers,
11 killallServers,
12 getUserAccessToken
13} from '../../utils'
14
15describe('Test request schedulers stats API validators', function () {
16 const path = '/api/v1/request-schedulers/stats'
17 let server = null
18 let userAccessToken = null
19
20 // ---------------------------------------------------------------
21
22 before(async function () {
23 this.timeout(60000)
24
25 await flushTests()
26
27 server = await runServer(1)
28 await setAccessTokensToServers([ server ])
29
30 const username = 'user'
31 const password = 'my super password'
32 await createUser(server.url, server.accessToken, username, password)
33
34 const user = {
35 username: 'user',
36 password: 'my super password'
37 }
38
39 userAccessToken = await getUserAccessToken(server, user)
40 })
41
42 it('Should fail with an non authenticated user', async function () {
43 await request(server.url)
44 .get(path)
45 .set('Accept', 'application/json')
46 .expect(401)
47 })
48
49 it('Should fail with a non admin user', async function () {
50 await request(server.url)
51 .get(path)
52 .set('Authorization', 'Bearer ' + userAccessToken)
53 .set('Accept', 'application/json')
54 .expect(403)
55 })
56
57 after(async function () {
58 killallServers([ server ])
59
60 // Keep the logs if the test failed
61 if (this['ok']) {
62 await flushTests()
63 }
64 })
65})
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 () {
473 473
474 it('Should fail with a video of another user') 474 it('Should fail with a video of another user')
475 475
476 it('Should fail with a video of another pod') 476 it('Should fail with a video of another server')
477 477
478 it('Should succeed with the correct parameters', async function () { 478 it('Should succeed with the correct parameters', async function () {
479 const fields = getCompleteVideoUpdateAttributes() 479 const fields = getCompleteVideoUpdateAttributes()
@@ -584,7 +584,7 @@ describe('Test videos API validator', function () {
584 584
585 it('Should fail with a video of another user') 585 it('Should fail with a video of another user')
586 586
587 it('Should fail with a video of another pod') 587 it('Should fail with a video of another server')
588 588
589 it('Should succeed with the correct parameters') 589 it('Should succeed with the correct parameters')
590 }) 590 })
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 @@
2import './config' 2import './config'
3import './check-params' 3import './check-params'
4import './users' 4import './users'
5import './single-pod' 5import './single-server'
6import './video-abuse' 6import './video-abuse'
7import './video-blacklist' 7import './video-blacklist'
8import './video-blacklist-management' 8import './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 @@
1// Order of the tests we want to execute 1// Order of the tests we want to execute
2import './multiple-pods' 2import './multiple-servers'
3import './video-transcoder' 3import './video-transcoder'
diff --git a/server/tests/api/multiple-pods.ts b/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 {
10 getVideo, 10 getVideo,
11 getVideosList, 11 getVideosList,
12 killallServers, 12 killallServers,
13 makeFriends,
14 rateVideo, 13 rateVideo,
15 removeVideo, 14 removeVideo,
16 ServerInfo, 15 ServerInfo,
@@ -22,13 +21,14 @@ import {
22 webtorrentAdd, 21 webtorrentAdd,
23 addVideoChannel, 22 addVideoChannel,
24 getVideoChannelsList, 23 getVideoChannelsList,
25 getUserAccessToken 24 getUserAccessToken,
25 doubleFollow
26} from '../utils' 26} from '../utils'
27import { createUser } from '../utils/users' 27import { createUser } from '../utils/users'
28 28
29const expect = chai.expect 29const expect = chai.expect
30 30
31describe('Test multiple pods', function () { 31describe('Test multiple servers', function () {
32 let servers: ServerInfo[] = [] 32 let servers: ServerInfo[] = []
33 const toRemove = [] 33 const toRemove = []
34 let videoUUID = '' 34 let videoUUID = ''
@@ -50,17 +50,15 @@ describe('Test multiple pods', function () {
50 const channelRes = await getVideoChannelsList(servers[0].url, 0, 1) 50 const channelRes = await getVideoChannelsList(servers[0].url, 0, 1)
51 videoChannelId = channelRes.body.data[0].id 51 videoChannelId = channelRes.body.data[0].id
52 52
53 // The second pod make friend with the third 53 // Server 1 and server 2 follow each other
54 await makeFriends(servers[1].url, servers[1].accessToken) 54 await doubleFollow(servers[0], servers[1])
55 55 // Server 1 and server 3 follow each other
56 // Wait for the request between pods 56 await doubleFollow(servers[0], servers[2])
57 await wait(10000) 57 // Server 2 and server 3 follow each other
58 58 await doubleFollow(servers[1], servers[2])
59 // Pod 1 make friends too
60 await makeFriends(servers[0].url, servers[0].accessToken)
61 }) 59 })
62 60
63 it('Should not have videos for all pods', async function () { 61 it('Should not have videos for all servers', async function () {
64 for (const server of servers) { 62 for (const server of servers) {
65 const res = await getVideosList(server.url) 63 const res = await getVideosList(server.url)
66 const videos = res.body.data 64 const videos = res.body.data
@@ -69,18 +67,18 @@ describe('Test multiple pods', function () {
69 } 67 }
70 }) 68 })
71 69
72 describe('Should upload the video and propagate on each pod', function () { 70 describe('Should upload the video and propagate on each server', function () {
73 it('Should upload the video on pod 1 and propagate on each pod', async function () { 71 it('Should upload the video on server 1 and propagate on each server', async function () {
74 // Pod 1 has video transcoding activated 72 // Server 1 has video transcoding activated
75 this.timeout(15000) 73 this.timeout(15000)
76 74
77 const videoAttributes = { 75 const videoAttributes = {
78 name: 'my super name for pod 1', 76 name: 'my super name for server 1',
79 category: 5, 77 category: 5,
80 licence: 4, 78 licence: 4,
81 language: 9, 79 language: 9,
82 nsfw: true, 80 nsfw: true,
83 description: 'my super description for pod 1', 81 description: 'my super description for server 1',
84 tags: [ 'tag1p1', 'tag2p1' ], 82 tags: [ 'tag1p1', 'tag2p1' ],
85 channelId: videoChannelId, 83 channelId: videoChannelId,
86 fixture: 'video_short1.webm' 84 fixture: 'video_short1.webm'
@@ -89,7 +87,7 @@ describe('Test multiple pods', function () {
89 87
90 await wait(11000) 88 await wait(11000)
91 89
92 // All pods should have this video 90 // All servers should have this video
93 for (const server of servers) { 91 for (const server of servers) {
94 let baseMagnet = null 92 let baseMagnet = null
95 93
@@ -99,7 +97,7 @@ describe('Test multiple pods', function () {
99 expect(videos).to.be.an('array') 97 expect(videos).to.be.an('array')
100 expect(videos.length).to.equal(1) 98 expect(videos.length).to.equal(1)
101 const video = videos[0] 99 const video = videos[0]
102 expect(video.name).to.equal('my super name for pod 1') 100 expect(video.name).to.equal('my super name for server 1')
103 expect(video.category).to.equal(5) 101 expect(video.category).to.equal(5)
104 expect(video.categoryLabel).to.equal('Sports') 102 expect(video.categoryLabel).to.equal('Sports')
105 expect(video.licence).to.equal(4) 103 expect(video.licence).to.equal(4)
@@ -107,8 +105,8 @@ describe('Test multiple pods', function () {
107 expect(video.language).to.equal(9) 105 expect(video.language).to.equal(9)
108 expect(video.languageLabel).to.equal('Japanese') 106 expect(video.languageLabel).to.equal('Japanese')
109 expect(video.nsfw).to.be.ok 107 expect(video.nsfw).to.be.ok
110 expect(video.description).to.equal('my super description for pod 1') 108 expect(video.description).to.equal('my super description for server 1')
111 expect(video.podHost).to.equal('localhost:9001') 109 expect(video.serverHost).to.equal('localhost:9001')
112 expect(video.duration).to.equal(10) 110 expect(video.duration).to.equal(10)
113 expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ]) 111 expect(video.tags).to.deep.equal([ 'tag1p1', 'tag2p1' ])
114 expect(dateIsValid(video.createdAt)).to.be.true 112 expect(dateIsValid(video.createdAt)).to.be.true
@@ -127,8 +125,9 @@ describe('Test multiple pods', function () {
127 const file = videoDetails.files[0] 125 const file = videoDetails.files[0]
128 const magnetUri = file.magnetUri 126 const magnetUri = file.magnetUri
129 expect(file.magnetUri).to.have.lengthOf.above(2) 127 expect(file.magnetUri).to.have.lengthOf.above(2)
130 expect(file.torrentUrl).to.equal(`http://${videoDetails.podHost}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`) 128 expect(file.torrentUrl).to
131 expect(file.fileUrl).to.equal(`http://${videoDetails.podHost}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`) 129 .equal(`http://${videoDetails.serverHost}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
130 expect(file.fileUrl).to.equal(`http://${videoDetails.serverHost}/static/webseed/${videoDetails.uuid}-${file.resolution}.webm`)
132 expect(file.resolution).to.equal(720) 131 expect(file.resolution).to.equal(720)
133 expect(file.resolutionLabel).to.equal('720p') 132 expect(file.resolutionLabel).to.equal('720p')
134 expect(file.size).to.equal(572456) 133 expect(file.size).to.equal(572456)
@@ -141,7 +140,7 @@ describe('Test multiple pods', function () {
141 expect(videoDetails.channel.isLocal).to.be.true 140 expect(videoDetails.channel.isLocal).to.be.true
142 } 141 }
143 142
144 // All pods should have the same magnet Uri 143 // All servers should have the same magnet Uri
145 if (baseMagnet === null) { 144 if (baseMagnet === null) {
146 baseMagnet = magnetUri 145 baseMagnet = magnetUri
147 } else { 146 } else {
@@ -153,7 +152,7 @@ describe('Test multiple pods', function () {
153 } 152 }
154 }) 153 })
155 154
156 it('Should upload the video on pod 2 and propagate on each pod', async function () { 155 it('Should upload the video on server 2 and propagate on each server', async function () {
157 this.timeout(120000) 156 this.timeout(120000)
158 157
159 const user = { 158 const user = {
@@ -164,12 +163,12 @@ describe('Test multiple pods', function () {
164 const userAccessToken = await getUserAccessToken(servers[1], user) 163 const userAccessToken = await getUserAccessToken(servers[1], user)
165 164
166 const videoAttributes = { 165 const videoAttributes = {
167 name: 'my super name for pod 2', 166 name: 'my super name for server 2',
168 category: 4, 167 category: 4,
169 licence: 3, 168 licence: 3,
170 language: 11, 169 language: 11,
171 nsfw: true, 170 nsfw: true,
172 description: 'my super description for pod 2', 171 description: 'my super description for server 2',
173 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], 172 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
174 fixture: 'video_short2.webm' 173 fixture: 'video_short2.webm'
175 } 174 }
@@ -178,7 +177,7 @@ describe('Test multiple pods', function () {
178 // Transcoding, so wait more than 22000 177 // Transcoding, so wait more than 22000
179 await wait(60000) 178 await wait(60000)
180 179
181 // All pods should have this video 180 // All servers should have this video
182 for (const server of servers) { 181 for (const server of servers) {
183 let baseMagnet = {} 182 let baseMagnet = {}
184 183
@@ -188,7 +187,7 @@ describe('Test multiple pods', function () {
188 expect(videos).to.be.an('array') 187 expect(videos).to.be.an('array')
189 expect(videos.length).to.equal(2) 188 expect(videos.length).to.equal(2)
190 const video = videos[1] 189 const video = videos[1]
191 expect(video.name).to.equal('my super name for pod 2') 190 expect(video.name).to.equal('my super name for server 2')
192 expect(video.category).to.equal(4) 191 expect(video.category).to.equal(4)
193 expect(video.categoryLabel).to.equal('Art') 192 expect(video.categoryLabel).to.equal('Art')
194 expect(video.licence).to.equal(3) 193 expect(video.licence).to.equal(3)
@@ -196,8 +195,8 @@ describe('Test multiple pods', function () {
196 expect(video.language).to.equal(11) 195 expect(video.language).to.equal(11)
197 expect(video.languageLabel).to.equal('German') 196 expect(video.languageLabel).to.equal('German')
198 expect(video.nsfw).to.be.true 197 expect(video.nsfw).to.be.true
199 expect(video.description).to.equal('my super description for pod 2') 198 expect(video.description).to.equal('my super description for server 2')
200 expect(video.podHost).to.equal('localhost:9002') 199 expect(video.serverHost).to.equal('localhost:9002')
201 expect(video.duration).to.equal(5) 200 expect(video.duration).to.equal(5)
202 expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ]) 201 expect(video.tags).to.deep.equal([ 'tag1p2', 'tag2p2', 'tag3p2' ])
203 expect(dateIsValid(video.createdAt)).to.be.true 202 expect(dateIsValid(video.createdAt)).to.be.true
@@ -223,7 +222,7 @@ describe('Test multiple pods', function () {
223 for (const file of videoDetails.files) { 222 for (const file of videoDetails.files) {
224 expect(file.magnetUri).to.have.lengthOf.above(2) 223 expect(file.magnetUri).to.have.lengthOf.above(2)
225 224
226 // All pods should have the same magnet Uri 225 // All servers should have the same magnet Uri
227 if (baseMagnet[file.resolution] === undefined) { 226 if (baseMagnet[file.resolution] === undefined) {
228 baseMagnet[file.resolution] = file.magnet 227 baseMagnet[file.resolution] = file.magnet
229 } else { 228 } else {
@@ -256,28 +255,28 @@ describe('Test multiple pods', function () {
256 } 255 }
257 }) 256 })
258 257
259 it('Should upload two videos on pod 3 and propagate on each pod', async function () { 258 it('Should upload two videos on server 3 and propagate on each server', async function () {
260 this.timeout(45000) 259 this.timeout(45000)
261 260
262 const videoAttributes1 = { 261 const videoAttributes1 = {
263 name: 'my super name for pod 3', 262 name: 'my super name for server 3',
264 category: 6, 263 category: 6,
265 licence: 5, 264 licence: 5,
266 language: 11, 265 language: 11,
267 nsfw: true, 266 nsfw: true,
268 description: 'my super description for pod 3', 267 description: 'my super description for server 3',
269 tags: [ 'tag1p3' ], 268 tags: [ 'tag1p3' ],
270 fixture: 'video_short3.webm' 269 fixture: 'video_short3.webm'
271 } 270 }
272 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1) 271 await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1)
273 272
274 const videoAttributes2 = { 273 const videoAttributes2 = {
275 name: 'my super name for pod 3-2', 274 name: 'my super name for server 3-2',
276 category: 7, 275 category: 7,
277 licence: 6, 276 licence: 6,
278 language: 12, 277 language: 12,
279 nsfw: false, 278 nsfw: false,
280 description: 'my super description for pod 3-2', 279 description: 'my super description for server 3-2',
281 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], 280 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
282 fixture: 'video_short.webm' 281 fixture: 'video_short.webm'
283 } 282 }
@@ -286,7 +285,7 @@ describe('Test multiple pods', function () {
286 await wait(33000) 285 await wait(33000)
287 286
288 let baseMagnet = null 287 let baseMagnet = null
289 // All pods should have this video 288 // All servers should have this video
290 for (const server of servers) { 289 for (const server of servers) {
291 const res = await getVideosList(server.url) 290 const res = await getVideosList(server.url)
292 291
@@ -297,7 +296,7 @@ describe('Test multiple pods', function () {
297 // We not sure about the order of the two last uploads 296 // We not sure about the order of the two last uploads
298 let video1 = null 297 let video1 = null
299 let video2 = null 298 let video2 = null
300 if (videos[2].name === 'my super name for pod 3') { 299 if (videos[2].name === 'my super name for server 3') {
301 video1 = videos[2] 300 video1 = videos[2]
302 video2 = videos[3] 301 video2 = videos[3]
303 } else { 302 } else {
@@ -305,7 +304,7 @@ describe('Test multiple pods', function () {
305 video2 = videos[2] 304 video2 = videos[2]
306 } 305 }
307 306
308 expect(video1.name).to.equal('my super name for pod 3') 307 expect(video1.name).to.equal('my super name for server 3')
309 expect(video1.category).to.equal(6) 308 expect(video1.category).to.equal(6)
310 expect(video1.categoryLabel).to.equal('Travels') 309 expect(video1.categoryLabel).to.equal('Travels')
311 expect(video1.licence).to.equal(5) 310 expect(video1.licence).to.equal(5)
@@ -313,8 +312,8 @@ describe('Test multiple pods', function () {
313 expect(video1.language).to.equal(11) 312 expect(video1.language).to.equal(11)
314 expect(video1.languageLabel).to.equal('German') 313 expect(video1.languageLabel).to.equal('German')
315 expect(video1.nsfw).to.be.ok 314 expect(video1.nsfw).to.be.ok
316 expect(video1.description).to.equal('my super description for pod 3') 315 expect(video1.description).to.equal('my super description for server 3')
317 expect(video1.podHost).to.equal('localhost:9003') 316 expect(video1.serverHost).to.equal('localhost:9003')
318 expect(video1.duration).to.equal(5) 317 expect(video1.duration).to.equal(5)
319 expect(video1.tags).to.deep.equal([ 'tag1p3' ]) 318 expect(video1.tags).to.deep.equal([ 'tag1p3' ])
320 expect(video1.author).to.equal('root') 319 expect(video1.author).to.equal('root')
@@ -331,7 +330,7 @@ describe('Test multiple pods', function () {
331 expect(file1.resolutionLabel).to.equal('720p') 330 expect(file1.resolutionLabel).to.equal('720p')
332 expect(file1.size).to.equal(292677) 331 expect(file1.size).to.equal(292677)
333 332
334 expect(video2.name).to.equal('my super name for pod 3-2') 333 expect(video2.name).to.equal('my super name for server 3-2')
335 expect(video2.category).to.equal(7) 334 expect(video2.category).to.equal(7)
336 expect(video2.categoryLabel).to.equal('Gaming') 335 expect(video2.categoryLabel).to.equal('Gaming')
337 expect(video2.licence).to.equal(6) 336 expect(video2.licence).to.equal(6)
@@ -339,8 +338,8 @@ describe('Test multiple pods', function () {
339 expect(video2.language).to.equal(12) 338 expect(video2.language).to.equal(12)
340 expect(video2.languageLabel).to.equal('Korean') 339 expect(video2.languageLabel).to.equal('Korean')
341 expect(video2.nsfw).to.be.false 340 expect(video2.nsfw).to.be.false
342 expect(video2.description).to.equal('my super description for pod 3-2') 341 expect(video2.description).to.equal('my super description for server 3-2')
343 expect(video2.podHost).to.equal('localhost:9003') 342 expect(video2.serverHost).to.equal('localhost:9003')
344 expect(video2.duration).to.equal(5) 343 expect(video2.duration).to.equal(5)
345 expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ]) 344 expect(video2.tags).to.deep.equal([ 'tag2p3', 'tag3p3', 'tag4p3' ])
346 expect(video2.author).to.equal('root') 345 expect(video2.author).to.equal('root')
@@ -367,7 +366,7 @@ describe('Test multiple pods', function () {
367 expect(video2.isLocal).to.be.true 366 expect(video2.isLocal).to.be.true
368 } 367 }
369 368
370 // All pods should have the same magnet Uri 369 // All servers should have the same magnet Uri
371 if (baseMagnet === null) { 370 if (baseMagnet === null) {
372 baseMagnet = magnetUri2 371 baseMagnet = magnetUri2
373 } else { 372 } else {
@@ -384,7 +383,7 @@ describe('Test multiple pods', function () {
384 }) 383 })
385 384
386 describe('Should seed the uploaded video', function () { 385 describe('Should seed the uploaded video', function () {
387 it('Should add the file 1 by asking pod 3', async function () { 386 it('Should add the file 1 by asking server 3', async function () {
388 // Yes, this could be long 387 // Yes, this could be long
389 this.timeout(200000) 388 this.timeout(200000)
390 389
@@ -403,7 +402,7 @@ describe('Test multiple pods', function () {
403 expect(torrent.files[0].path).to.exist.and.to.not.equal('') 402 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
404 }) 403 })
405 404
406 it('Should add the file 2 by asking pod 1', async function () { 405 it('Should add the file 2 by asking server 1', async function () {
407 // Yes, this could be long 406 // Yes, this could be long
408 this.timeout(200000) 407 this.timeout(200000)
409 408
@@ -419,7 +418,7 @@ describe('Test multiple pods', function () {
419 expect(torrent.files[0].path).to.exist.and.to.not.equal('') 418 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
420 }) 419 })
421 420
422 it('Should add the file 3 by asking pod 2', async function () { 421 it('Should add the file 3 by asking server 2', async function () {
423 // Yes, this could be long 422 // Yes, this could be long
424 this.timeout(200000) 423 this.timeout(200000)
425 424
@@ -435,7 +434,7 @@ describe('Test multiple pods', function () {
435 expect(torrent.files[0].path).to.exist.and.to.not.equal('') 434 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
436 }) 435 })
437 436
438 it('Should add the file 3-2 by asking pod 1', async function () { 437 it('Should add the file 3-2 by asking server 1', async function () {
439 // Yes, this could be long 438 // Yes, this could be long
440 this.timeout(200000) 439 this.timeout(200000)
441 440
@@ -451,13 +450,13 @@ describe('Test multiple pods', function () {
451 expect(torrent.files[0].path).to.exist.and.to.not.equal('') 450 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
452 }) 451 })
453 452
454 it('Should add the file 2 in 360p by asking pod 1', async function () { 453 it('Should add the file 2 in 360p by asking server 1', async function () {
455 // Yes, this could be long 454 // Yes, this could be long
456 this.timeout(200000) 455 this.timeout(200000)
457 456
458 const res = await getVideosList(servers[0].url) 457 const res = await getVideosList(servers[0].url)
459 458
460 const video = res.body.data.find(v => v.name === 'my super name for pod 2') 459 const video = res.body.data.find(v => v.name === 'my super name for server 2')
461 const res2 = await getVideo(servers[0].url, video.id) 460 const res2 = await getVideo(servers[0].url, video.id)
462 const videoDetails = res2.body 461 const videoDetails = res2.body
463 462
@@ -472,31 +471,31 @@ describe('Test multiple pods', function () {
472 }) 471 })
473 472
474 describe('Should update video views, likes and dislikes', function () { 473 describe('Should update video views, likes and dislikes', function () {
475 let localVideosPod3 = [] 474 let localVideosServer3 = []
476 let remoteVideosPod1 = [] 475 let remoteVideosServer1 = []
477 let remoteVideosPod2 = [] 476 let remoteVideosServer2 = []
478 let remoteVideosPod3 = [] 477 let remoteVideosServer3 = []
479 478
480 before(async function () { 479 before(async function () {
481 const res1 = await getVideosList(servers[0].url) 480 const res1 = await getVideosList(servers[0].url)
482 remoteVideosPod1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 481 remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
483 482
484 const res2 = await getVideosList(servers[1].url) 483 const res2 = await getVideosList(servers[1].url)
485 remoteVideosPod2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 484 remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
486 485
487 const res3 = await getVideosList(servers[2].url) 486 const res3 = await getVideosList(servers[2].url)
488 localVideosPod3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid) 487 localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid)
489 remoteVideosPod3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid) 488 remoteVideosServer3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
490 }) 489 })
491 490
492 it('Should view multiple videos on owned servers', async function () { 491 it('Should view multiple videos on owned servers', async function () {
493 this.timeout(30000) 492 this.timeout(30000)
494 493
495 const tasks: Promise<any>[] = [] 494 const tasks: Promise<any>[] = []
496 tasks.push(getVideo(servers[2].url, localVideosPod3[0])) 495 tasks.push(getVideo(servers[2].url, localVideosServer3[0]))
497 tasks.push(getVideo(servers[2].url, localVideosPod3[0])) 496 tasks.push(getVideo(servers[2].url, localVideosServer3[0]))
498 tasks.push(getVideo(servers[2].url, localVideosPod3[0])) 497 tasks.push(getVideo(servers[2].url, localVideosServer3[0]))
499 tasks.push(getVideo(servers[2].url, localVideosPod3[1])) 498 tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
500 499
501 await Promise.all(tasks) 500 await Promise.all(tasks)
502 501
@@ -506,8 +505,8 @@ describe('Test multiple pods', function () {
506 const res = await getVideosList(server.url) 505 const res = await getVideosList(server.url)
507 506
508 const videos = res.body.data 507 const videos = res.body.data
509 const video0 = videos.find(v => v.uuid === localVideosPod3[0]) 508 const video0 = videos.find(v => v.uuid === localVideosServer3[0])
510 const video1 = videos.find(v => v.uuid === localVideosPod3[1]) 509 const video1 = videos.find(v => v.uuid === localVideosServer3[1])
511 510
512 expect(video0.views).to.equal(7) 511 expect(video0.views).to.equal(7)
513 expect(video1.views).to.equal(5) 512 expect(video1.views).to.equal(5)
@@ -518,16 +517,16 @@ describe('Test multiple pods', function () {
518 this.timeout(30000) 517 this.timeout(30000)
519 518
520 const tasks: Promise<any>[] = [] 519 const tasks: Promise<any>[] = []
521 tasks.push(getVideo(servers[0].url, remoteVideosPod1[0])) 520 tasks.push(getVideo(servers[0].url, remoteVideosServer1[0]))
522 tasks.push(getVideo(servers[1].url, remoteVideosPod2[0])) 521 tasks.push(getVideo(servers[1].url, remoteVideosServer2[0]))
523 tasks.push(getVideo(servers[1].url, remoteVideosPod2[0])) 522 tasks.push(getVideo(servers[1].url, remoteVideosServer2[0]))
524 tasks.push(getVideo(servers[2].url, remoteVideosPod3[0])) 523 tasks.push(getVideo(servers[2].url, remoteVideosServer3[0]))
525 tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) 524 tasks.push(getVideo(servers[2].url, remoteVideosServer3[1]))
526 tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) 525 tasks.push(getVideo(servers[2].url, remoteVideosServer3[1]))
527 tasks.push(getVideo(servers[2].url, remoteVideosPod3[1])) 526 tasks.push(getVideo(servers[2].url, remoteVideosServer3[1]))
528 tasks.push(getVideo(servers[2].url, localVideosPod3[1])) 527 tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
529 tasks.push(getVideo(servers[2].url, localVideosPod3[1])) 528 tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
530 tasks.push(getVideo(servers[2].url, localVideosPod3[1])) 529 tasks.push(getVideo(servers[2].url, localVideosServer3[1]))
531 530
532 await Promise.all(tasks) 531 await Promise.all(tasks)
533 532
@@ -557,13 +556,13 @@ describe('Test multiple pods', function () {
557 this.timeout(30000) 556 this.timeout(30000)
558 557
559 const tasks: Promise<any>[] = [] 558 const tasks: Promise<any>[] = []
560 tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like')) 559 tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
561 tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'dislike')) 560 tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike'))
562 tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like')) 561 tasks.push(rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like'))
563 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'like')) 562 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like'))
564 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'dislike')) 563 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike'))
565 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[1], 'dislike')) 564 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike'))
566 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[0], 'like')) 565 tasks.push(rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like'))
567 566
568 await Promise.all(tasks) 567 await Promise.all(tasks)
569 568
@@ -591,7 +590,7 @@ describe('Test multiple pods', function () {
591 }) 590 })
592 591
593 describe('Should manipulate these videos', function () { 592 describe('Should manipulate these videos', function () {
594 it('Should update the video 3 by asking pod 3', async function () { 593 it('Should update the video 3 by asking server 3', async function () {
595 this.timeout(15000) 594 this.timeout(15000)
596 595
597 const attributes = { 596 const attributes = {
@@ -609,7 +608,7 @@ describe('Test multiple pods', function () {
609 await wait(11000) 608 await wait(11000)
610 }) 609 })
611 610
612 it('Should have the video 3 updated on each pod', async function () { 611 it('Should have the video 3 updated on each server', async function () {
613 this.timeout(200000) 612 this.timeout(200000)
614 613
615 for (const server of servers) { 614 for (const server of servers) {
@@ -651,7 +650,7 @@ describe('Test multiple pods', function () {
651 } 650 }
652 }) 651 })
653 652
654 it('Should remove the videos 3 and 3-2 by asking pod 3', async function () { 653 it('Should remove the videos 3 and 3-2 by asking server 3', async function () {
655 this.timeout(15000) 654 this.timeout(15000)
656 655
657 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id) 656 await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id)
@@ -660,7 +659,7 @@ describe('Test multiple pods', function () {
660 await wait(11000) 659 await wait(11000)
661 }) 660 })
662 661
663 it('Should have videos 1 and 3 on each pod', async function () { 662 it('Should have videos 1 and 3 on each server', async function () {
664 for (const server of servers) { 663 for (const server of servers) {
665 const res = await getVideosList(server.url) 664 const res = await getVideosList(server.url)
666 665
@@ -673,11 +672,11 @@ describe('Test multiple pods', function () {
673 expect(videos[0].name).not.to.equal(toRemove[1].name) 672 expect(videos[0].name).not.to.equal(toRemove[1].name)
674 expect(videos[1].name).not.to.equal(toRemove[1].name) 673 expect(videos[1].name).not.to.equal(toRemove[1].name)
675 674
676 videoUUID = videos.find(video => video.name === 'my super name for pod 1').uuid 675 videoUUID = videos.find(video => video.name === 'my super name for server 1').uuid
677 } 676 }
678 }) 677 })
679 678
680 it('Should get the same video by UUID on each pod', async function () { 679 it('Should get the same video by UUID on each server', async function () {
681 let baseVideo = null 680 let baseVideo = null
682 for (const server of servers) { 681 for (const server of servers) {
683 const res = await getVideo(server.url, videoUUID) 682 const res = await getVideo(server.url, videoUUID)
@@ -701,7 +700,7 @@ describe('Test multiple pods', function () {
701 } 700 }
702 }) 701 })
703 702
704 it('Should get the preview from each pod', async function () { 703 it('Should get the preview from each server', async function () {
705 for (const server of servers) { 704 for (const server of servers) {
706 const res = await getVideo(server.url, videoUUID) 705 const res = await getVideo(server.url, videoUUID)
707 const video = res.body 706 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 {
14 getOEmbed 14 getOEmbed
15} from '../utils' 15} from '../utils'
16import { runServer } from '../utils/servers' 16import { runServer } from '../utils/servers'
17import { Video } from '../../../client/src/app/videos/shared/video.model'
18 17
19describe('Test services', function () { 18describe('Test services', function () {
20 let server: ServerInfo = null 19 let server: ServerInfo = null
diff --git a/server/tests/api/single-pod.ts b/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 {
34 updateVideo 34 updateVideo
35} from '../utils' 35} from '../utils'
36 36
37describe('Test a single pod', function () { 37describe('Test a single server', function () {
38 let server: ServerInfo = null 38 let server: ServerInfo = null
39 let videoId = -1 39 let videoId = -1
40 let videoUUID = '' 40 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 () {
40 }) 40 })
41 41
42 it('Should upload a private video on server 1', async function () { 42 it('Should upload a private video on server 1', async function () {
43 this.timeout(15000) 43 this.timeout(25000)
44 44
45 const attributes = { 45 const attributes = {
46 privacy: VideoPrivacy.PRIVATE 46 privacy: VideoPrivacy.PRIVATE
47 } 47 }
48 await uploadVideo(servers[0].url, servers[0].accessToken, attributes) 48 await uploadVideo(servers[0].url, servers[0].accessToken, attributes)
49 49
50 await wait(11000) 50 await wait(15000)
51 }) 51 })
52 52
53 it('Should not have this private video on server 2', async function () { 53 it('Should not have this private video on server 2', async function () {
@@ -86,8 +86,8 @@ describe('Test video privacy', function () {
86 await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID) 86 await getVideoWithToken(servers[0].url, servers[0].accessToken, privateVideoUUID)
87 }) 87 })
88 88
89 it('Should upload a unlisted video on server 2', async function () { 89 it('Should upload an unlisted video on server 2', async function () {
90 this.timeout(30000) 90 this.timeout(50000)
91 91
92 const attributes = { 92 const attributes = {
93 name: 'unlisted video', 93 name: 'unlisted video',
@@ -95,7 +95,7 @@ describe('Test video privacy', function () {
95 } 95 }
96 await uploadVideo(servers[1].url, servers[1].accessToken, attributes) 96 await uploadVideo(servers[1].url, servers[1].accessToken, attributes)
97 97
98 await wait(22000) 98 await wait(40000)
99 }) 99 })
100 100
101 it('Should not have this unlisted video listed on server 1 and 2', async function () { 101 it('Should not have this unlisted video listed on server 1 and 2', async function () {
@@ -125,7 +125,7 @@ describe('Test video privacy', function () {
125 }) 125 })
126 126
127 it('Should update the private video to public on server 1', async function () { 127 it('Should update the private video to public on server 1', async function () {
128 this.timeout(15000) 128 this.timeout(40000)
129 129
130 const attribute = { 130 const attribute = {
131 name: 'super video public', 131 name: 'super video public',
@@ -134,10 +134,10 @@ describe('Test video privacy', function () {
134 134
135 await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute) 135 await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, attribute)
136 136
137 await wait(11000) 137 await wait(30000)
138 }) 138 })
139 139
140 it('Should not have this new unlisted video listed on server 1 and 2', async function () { 140 it('Should have this new public video listed on server 1 and 2', async function () {
141 for (const server of servers) { 141 for (const server of servers) {
142 const res = await getVideosList(server.url) 142 const res = await getVideosList(server.url)
143 143
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 () {
26 server.accessToken = await loginAndGetAccessToken(server) 26 server.accessToken = await loginAndGetAccessToken(server)
27 27
28 const videoAttributes = { 28 const videoAttributes = {
29 name: 'my super name for pod 1', 29 name: 'my super name for server 1',
30 description: 'my super description for pod 1' 30 description: 'my super description for server 1'
31 } 31 }
32 await uploadVideo(server.url, server.accessToken, videoAttributes) 32 await uploadVideo(server.url, server.accessToken, videoAttributes)
33 33
@@ -44,8 +44,8 @@ describe('Test a client controllers', function () {
44 .get('/videos/watch/' + server.video.id) 44 .get('/videos/watch/' + server.video.id)
45 .expect(200) 45 .expect(200)
46 46
47 expect(res.text).to.contain('<meta property="og:title" content="my super name for pod 1" />') 47 expect(res.text).to.contain('<meta property="og:title" content="my super name for server 1" />')
48 expect(res.text).to.contain('<meta property="og:description" content="my super description for pod 1" />') 48 expect(res.text).to.contain('<meta property="og:description" content="my super description for server 1" />')
49 }) 49 })
50 50
51 it('Should have valid Open Graph tags on the watch page with video uuid', async function () { 51 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 () {
53 .get('/videos/watch/' + server.video.uuid) 53 .get('/videos/watch/' + server.video.uuid)
54 .expect(200) 54 .expect(200)
55 55
56 expect(res.text).to.contain('<meta property="og:title" content="my super name for pod 1" />') 56 expect(res.text).to.contain('<meta property="og:title" content="my super name for server 1" />')
57 expect(res.text).to.contain('<meta property="og:description" content="my super description for pod 1" />') 57 expect(res.text).to.contain('<meta property="og:description" content="my super description for server 1" />')
58 }) 58 })
59 59
60 it('Should have valid oEmbed discovery tags', async function () { 60 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 @@
1import * as program from 'commander'
2
3// /!\ Before imports /!\ 1// /!\ Before imports /!\
4process.env.NODE_ENV = 'test' 2process.env.NODE_ENV = 'test'
5 3
6import { Video, VideoRateType, VideoFile } from '../../../shared' 4import * as program from 'commander'
5import { Video, VideoFile, VideoRateType } from '../../../shared'
7import { 6import {
8 ServerInfo as DefaultServerInfo,
9 flushAndRunMultipleServers, 7 flushAndRunMultipleServers,
10 setAccessTokensToServers,
11 makeFriends,
12 wait,
13 killallServers,
14 flushTests, 8 flushTests,
15 uploadVideo, 9 getAllVideosListBy,
10 getRequestsStats,
11 getVideo,
16 getVideosList, 12 getVideosList,
17 updateVideo, 13 killallServers,
18 removeVideo, 14 removeVideo,
19 getVideo, 15 ServerInfo as DefaultServerInfo,
20 getAllVideosListBy, 16 setAccessTokensToServers,
21 getRequestsStats 17 updateVideo,
18 uploadVideo,
19 wait
22} from '../utils' 20} from '../utils'
21import { follow } from '../utils/follows'
23 22
24interface ServerInfo extends DefaultServerInfo { 23interface ServerInfo extends DefaultServerInfo {
25 requestsNumber: number 24 requestsNumber: number
@@ -32,7 +31,7 @@ program
32 .option('-v, --view [weight]', 'Weight for viewing videos') 31 .option('-v, --view [weight]', 'Weight for viewing videos')
33 .option('-l, --like [weight]', 'Weight for liking videos') 32 .option('-l, --like [weight]', 'Weight for liking videos')
34 .option('-s, --dislike [weight]', 'Weight for disliking videos') 33 .option('-s, --dislike [weight]', 'Weight for disliking videos')
35 .option('-p, --pods [n]', 'Number of pods to run (3 or 6)', /^3|6$/, 3) 34 .option('-p, --servers [n]', 'Number of servers to run (3 or 6)', /^3|6$/, 3)
36 .option('-i, --interval-action [interval]', 'Interval in ms for an action') 35 .option('-i, --interval-action [interval]', 'Interval in ms for an action')
37 .option('-I, --interval-integrity [interval]', 'Interval in ms for an integrity check') 36 .option('-I, --interval-integrity [interval]', 'Interval in ms for an integrity check')
38 .option('-f, --flush', 'Flush datas on exit') 37 .option('-f, --flush', 'Flush datas on exit')
@@ -50,7 +49,7 @@ const actionInterval = program['intervalAction'] !== undefined ? parseInt(progra
50const integrityInterval = program['intervalIntegrity'] !== undefined ? parseInt(program['intervalIntegrity'], 10) : 60000 49const integrityInterval = program['intervalIntegrity'] !== undefined ? parseInt(program['intervalIntegrity'], 10) : 60000
51const displayDiffOnFail = program['difference'] || false 50const displayDiffOnFail = program['difference'] || false
52 51
53const numberOfPods = 6 52const numberOfServers = 6
54 53
55console.log( 54console.log(
56 'Create weight: %d, update weight: %d, remove weight: %d, view weight: %d, like weight: %d, dislike weight: %d.', 55 'Create weight: %d, update weight: %d, remove weight: %d, view weight: %d, like weight: %d, dislike weight: %d.',
@@ -77,7 +76,7 @@ start()
77// ---------------------------------------------------------------------------- 76// ----------------------------------------------------------------------------
78 77
79async function start () { 78async function start () {
80 const servers = await runServers(numberOfPods) 79 const servers = await runServers(numberOfServers)
81 80
82 process.on('exit', async () => { 81 process.on('exit', async () => {
83 await exitServers(servers, flushAtExit) 82 await exitServers(servers, flushAtExit)
@@ -152,22 +151,20 @@ function getRandomNumServer (servers) {
152 return getRandomInt(0, servers.length) 151 return getRandomInt(0, servers.length)
153} 152}
154 153
155async function runServers (numberOfPods: number) { 154async function runServers (numberOfServers: number) {
156 const servers: ServerInfo[] = (await flushAndRunMultipleServers(numberOfPods)) 155 const servers: ServerInfo[] = (await flushAndRunMultipleServers(numberOfServers))
157 .map(s => Object.assign({ requestsNumber: 0 }, s)) 156 .map(s => Object.assign({ requestsNumber: 0 }, s))
158 157
159 // Get the access tokens 158 // Get the access tokens
160 await setAccessTokensToServers(servers) 159 await setAccessTokensToServers(servers)
161 160
162 await makeFriends(servers[1].url, servers[1].accessToken) 161 for (let i = 0; i < numberOfServers; i++) {
163 await makeFriends(servers[0].url, servers[0].accessToken) 162 for (let j = 0; j < numberOfServers; j++) {
164 await wait(1000) 163 if (i === j) continue
165
166 await makeFriends(servers[3].url, servers[3].accessToken)
167 await makeFriends(servers[5].url, servers[5].accessToken)
168 await makeFriends(servers[4].url, servers[4].accessToken)
169 164
170 await wait(1000) 165 await follow(servers[i].url, [ servers[j].url ], servers[i].accessToken)
166 }
167 }
171 168
172 return servers 169 return servers
173} 170}
@@ -259,7 +256,7 @@ async function checkIntegrity (servers: ServerInfo[]) {
259 const videos: Video[][] = [] 256 const videos: Video[][] = []
260 const tasks: Promise<any>[] = [] 257 const tasks: Promise<any>[] = []
261 258
262 // Fetch all videos and remove some fields that can differ between pods 259 // Fetch all videos and remove some fields that can differ between servers
263 for (const server of servers) { 260 for (const server of servers) {
264 const p = getAllVideosListBy(server.url).then(res => videos.push(res.body.data)) 261 const p = getAllVideosListBy(server.url).then(res => videos.push(res.body.data))
265 tasks.push(p) 262 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
29} 29}
30 30
31async function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) { 31async function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) {
32 const path = '/api/v1/server/follow' 32 const path = '/api/v1/server/following'
33 33
34 const followingHosts = following.map(f => f.replace(/^http:\/\//, '')) 34 const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
35 const res = await request(follower) 35 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 {
12 '@context'?: any[] 12 '@context'?: any[]
13 id: string 13 id: string
14 to?: string[] 14 to?: string[]
15 cc?: string[]
15 actor: string 16 actor: string
16 type: ActivityType 17 type: ActivityType
17 signature?: ActivityPubSignature 18 signature?: ActivityPubSignature