Refractor activities sending

pull/1074/head
Chocobozzz 2018-09-14 16:51:35 +02:00
parent d61b817890
commit a2377d15ee
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
11 changed files with 140 additions and 155 deletions

View File

@ -393,9 +393,9 @@ async function viewVideo (req: express.Request, res: express.Response) {
Redis.Instance.setIPVideoView(ip, videoInstance.uuid)
])
const serverAccount = await getServerActor()
const serverActor = await getServerActor()
await sendCreateView(serverAccount, videoInstance, undefined)
await sendCreateView(serverActor, videoInstance, undefined)
return res.status(204).end()
}

View File

@ -6,7 +6,7 @@ import { VideoModel } from '../../models/video/video'
import { VideoCommentModel } from '../../models/video/video-comment'
import { VideoShareModel } from '../../models/video/video-share'
function getVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) {
function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience {
return {
to: [ video.VideoChannel.Account.Actor.url ],
cc: actorsInvolvedInVideo.map(a => a.followersUrl)
@ -18,7 +18,7 @@ function getVideoCommentAudience (
threadParentComments: VideoCommentModel[],
actorsInvolvedInVideo: ActorModel[],
isOrigin = false
) {
): ActivityAudience {
const to = [ ACTIVITY_PUB.PUBLIC ]
const cc: string[] = []
@ -41,7 +41,7 @@ function getVideoCommentAudience (
}
}
function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) {
function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience {
return {
to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
cc: []
@ -83,9 +83,9 @@ function audiencify<T> (object: T, audience: ActivityAudience) {
export {
buildAudience,
getAudience,
getVideoAudience,
getRemoteVideoAudience,
getActorsInvolvedInVideo,
getObjectFollowersAudience,
getAudienceFromFollowersOf,
audiencify,
getVideoCommentAudience
}

View File

@ -41,6 +41,8 @@ async function processDeleteActivity (activity: ActivityDelete) {
{
const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(objectUrl)
if (videoInstance) {
if (videoInstance.isOwned()) throw new Error(`Remote instance cannot delete owned video ${videoInstance.url}.`)
return retryTransactionWrapper(processDeleteVideo, actor, videoInstance)
}
}

View File

@ -4,14 +4,14 @@ import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
import { VideoShareModel } from '../../../models/video/video-share'
import { broadcastToFollowers } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from '../audience'
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
import { logger } from '../../../helpers/logger'
async function buildAnnounceWithVideoAudience (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
const announcedObject = video.url
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
const activity = buildAnnounceActivity(videoShare.url, byActor, announcedObject, audience)

View File

@ -1,21 +1,13 @@
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
import { VideoPrivacy } from '../../../../shared/models/videos'
import { getServerActor } from '../../../helpers/utils'
import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
import { VideoAbuseModel } from '../../../models/video/video-abuse'
import { VideoCommentModel } from '../../../models/video/video-comment'
import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils'
import {
audiencify,
getActorsInvolvedInVideo,
getAudience,
getObjectFollowersAudience,
getVideoAudience,
getVideoCommentAudience
} from '../audience'
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
import { logger } from '../../../helpers/logger'
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
@ -40,6 +32,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
logger.info('Creating job to send video abuse %s.', url)
// Custom audience, we only send the abuse to the origin instance
const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] }
const createActivity = buildCreateActivity(url, byActor, videoAbuse.toActivityPubObject(), audience)
@ -49,15 +42,15 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
async function sendCreateCacheFile (byActor: ActorModel, fileRedundancy: VideoRedundancyModel) {
logger.info('Creating job to send file cache of %s.', fileRedundancy.url)
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(fileRedundancy.VideoFile.Video.id)
const redundancyObject = fileRedundancy.toActivityPubObject()
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(fileRedundancy.VideoFile.Video.id)
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, undefined)
const audience = getVideoAudience(video, actorsInvolvedInVideo)
const createActivity = buildCreateActivity(fileRedundancy.url, byActor, redundancyObject, audience)
return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
return sendVideoRelatedCreateActivity({
byActor,
video,
url: fileRedundancy.url,
object: redundancyObject
})
}
async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) {
@ -70,6 +63,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio
const commentObject = comment.toActivityPubObject(threadParentComments)
const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
// Add the actor that commented too
actorsInvolvedInComment.push(byActor)
const parentsCommentActors = threadParentComments.map(c => c.Account.Actor)
@ -78,7 +72,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio
if (isOrigin) {
audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment, isOrigin)
} else {
audience = getObjectFollowersAudience(actorsInvolvedInComment.concat(parentsCommentActors))
audience = getAudienceFromFollowersOf(actorsInvolvedInComment.concat(parentsCommentActors))
}
const createActivity = buildCreateActivity(comment.url, byActor, commentObject, audience)
@ -103,24 +97,14 @@ async function sendCreateView (byActor: ActorModel, video: VideoModel, t: Transa
const url = getVideoViewActivityPubUrl(byActor, video)
const viewActivity = buildViewActivity(byActor, video)
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
// Send to origin
if (video.isOwned() === false) {
const audience = getVideoAudience(video, actorsInvolvedInVideo)
const createActivity = buildCreateActivity(url, byActor, viewActivity, audience)
return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
}
// Send to followers
const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
const createActivity = buildCreateActivity(url, byActor, viewActivity, audience)
// Use the server actor to send the view
const serverActor = await getServerActor()
const actorsException = [ byActor ]
return broadcastToFollowers(createActivity, serverActor, actorsInvolvedInVideo, t, actorsException)
return sendVideoRelatedCreateActivity({
// Use the server actor to send the view
byActor,
video,
url,
object: viewActivity,
transaction: t
})
}
async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
@ -129,22 +113,13 @@ async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Tra
const url = getVideoDislikeActivityPubUrl(byActor, video)
const dislikeActivity = buildDislikeActivity(byActor, video)
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
// Send to origin
if (video.isOwned() === false) {
const audience = getVideoAudience(video, actorsInvolvedInVideo)
const createActivity = buildCreateActivity(url, byActor, dislikeActivity, audience)
return unicastTo(createActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
}
// Send to followers
const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
const createActivity = buildCreateActivity(url, byActor, dislikeActivity, audience)
const actorsException = [ byActor ]
return broadcastToFollowers(createActivity, byActor, actorsInvolvedInVideo, t, actorsException)
return sendVideoRelatedCreateActivity({
byActor,
video,
url,
object: dislikeActivity,
transaction: t
})
}
function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate {
@ -189,3 +164,19 @@ export {
sendCreateVideoComment,
sendCreateCacheFile
}
// ---------------------------------------------------------------------------
async function sendVideoRelatedCreateActivity (options: {
byActor: ActorModel,
video: VideoModel,
url: string,
object: any,
transaction?: Transaction
}) {
const activityBuilder = (audience: ActivityAudience) => {
return buildCreateActivity(options.url, options.byActor, options.object, audience)
}
return sendVideoRelatedActivity(activityBuilder, options)
}

View File

@ -5,21 +5,22 @@ import { VideoModel } from '../../../models/video/video'
import { VideoCommentModel } from '../../../models/video/video-comment'
import { VideoShareModel } from '../../../models/video/video-share'
import { getDeleteActivityPubUrl } from '../url'
import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils'
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
import { logger } from '../../../helpers/logger'
async function sendDeleteVideo (video: VideoModel, t: Transaction) {
async function sendDeleteVideo (video: VideoModel, transaction: Transaction) {
logger.info('Creating job to broadcast delete of video %s.', video.url)
const url = getDeleteActivityPubUrl(video.url)
const byActor = video.VideoChannel.Account.Actor
const activity = buildDeleteActivity(url, video.url, byActor)
const activityBuilder = (audience: ActivityAudience) => {
const url = getDeleteActivityPubUrl(video.url)
const actorsInvolved = await getActorsInvolvedInVideo(video, t)
return buildDeleteActivity(url, video.url, byActor, audience)
}
return broadcastToFollowers(activity, byActor, actorsInvolved, t)
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction })
}
async function sendDeleteActor (byActor: ActorModel, t: Transaction) {

View File

@ -3,31 +3,20 @@ import { ActivityAudience, ActivityLike } from '../../../../shared/models/activi
import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
import { getVideoLikeActivityPubUrl } from '../url'
import { broadcastToFollowers, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience'
import { sendVideoRelatedActivity } from './utils'
import { audiencify, getAudience } from '../audience'
import { logger } from '../../../helpers/logger'
async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to like %s.', video.url)
const url = getVideoLikeActivityPubUrl(byActor, video)
const activityBuilder = (audience: ActivityAudience) => {
const url = getVideoLikeActivityPubUrl(byActor, video)
const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
// Send to origin
if (video.isOwned() === false) {
const audience = getVideoAudience(video, accountsInvolvedInVideo)
const data = buildLikeActivity(url, byActor, video, audience)
return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
return buildLikeActivity(url, byActor, video, audience)
}
// Send to followers
const audience = getObjectFollowersAudience(accountsInvolvedInVideo)
const activity = buildLikeActivity(url, byActor, video, audience)
const followersException = [ byActor ]
return broadcastToFollowers(activity, byActor, accountsInvolvedInVideo, t, followersException)
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
}
function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike {

View File

@ -11,8 +11,8 @@ import { ActorModel } from '../../../models/activitypub/actor'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { VideoModel } from '../../../models/video/video'
import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
import { broadcastToFollowers, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience'
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getAudience } from '../audience'
import { buildCreateActivity, buildDislikeActivity } from './send-create'
import { buildFollowActivity } from './send-follow'
import { buildLikeActivity } from './send-like'
@ -39,53 +39,6 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
return unicastTo(undoActivity, me, following.inboxUrl)
}
async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to undo a like of video %s.', video.url)
const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
const undoUrl = getUndoActivityPubUrl(likeUrl)
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
const likeActivity = buildLikeActivity(likeUrl, byActor, video)
// Send to origin
if (video.isOwned() === false) {
const audience = getVideoAudience(video, actorsInvolvedInVideo)
const undoActivity = undoActivityData(undoUrl, byActor, likeActivity, audience)
return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
}
const audience = getObjectFollowersAudience(actorsInvolvedInVideo)
const undoActivity = undoActivityData(undoUrl, byActor, likeActivity, audience)
const followersException = [ byActor ]
return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
}
async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to undo a dislike of video %s.', video.url)
const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
const undoUrl = getUndoActivityPubUrl(dislikeUrl)
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
const dislikeActivity = buildDislikeActivity(byActor, video)
const createDislikeActivity = buildCreateActivity(dislikeUrl, byActor, dislikeActivity)
if (video.isOwned() === false) {
const audience = getVideoAudience(video, actorsInvolvedInVideo)
const undoActivity = undoActivityData(undoUrl, byActor, createDislikeActivity, audience)
return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
}
const undoActivity = undoActivityData(undoUrl, byActor, createDislikeActivity)
const followersException = [ byActor ]
return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
}
async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to undo announce %s.', videoShare.url)
@ -98,20 +51,32 @@ async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareMode
return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
}
async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to undo a like of video %s.', video.url)
const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
const likeActivity = buildLikeActivity(likeUrl, byActor, video)
return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
}
async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) {
logger.info('Creating job to undo a dislike of video %s.', video.url)
const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
const dislikeActivity = buildDislikeActivity(byActor, video)
const createDislikeActivity = buildCreateActivity(dislikeUrl, byActor, dislikeActivity)
return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: createDislikeActivity, transaction: t })
}
async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) {
logger.info('Creating job to undo cache file %s.', redundancyModel.url)
const undoUrl = getUndoActivityPubUrl(redundancyModel.url)
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id)
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
const audience = getVideoAudience(video, actorsInvolvedInVideo)
const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject())
const undoActivity = undoActivityData(undoUrl, byActor, createActivity, audience)
return unicastTo(undoActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t })
}
// ---------------------------------------------------------------------------
@ -144,3 +109,19 @@ function undoActivityData (
audience
)
}
async function sendUndoVideoRelatedActivity (options: {
byActor: ActorModel,
video: VideoModel,
url: string,
activity: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce,
transaction: Transaction
}) {
const activityBuilder = (audience: ActivityAudience) => {
const undoUrl = getUndoActivityPubUrl(options.url)
return undoActivityData(undoUrl, options.byActor, options.activity, audience)
}
return sendVideoRelatedActivity(activityBuilder, options)
}

View File

@ -7,8 +7,8 @@ import { VideoModel } from '../../../models/video/video'
import { VideoChannelModel } from '../../../models/video/video-channel'
import { VideoShareModel } from '../../../models/video/video-share'
import { getUpdateActivityPubUrl } from '../url'
import { broadcastToFollowers, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from '../audience'
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
import { logger } from '../../../helpers/logger'
import { VideoCaptionModel } from '../../../models/video/video-caption'
import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
@ -61,16 +61,16 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) {
logger.info('Creating job to update cache file %s.', redundancyModel.url)
const url = getUpdateActivityPubUrl(redundancyModel.url, redundancyModel.updatedAt.toISOString())
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.VideoFile.Video.id)
const redundancyObject = redundancyModel.toActivityPubObject()
const activityBuilder = (audience: ActivityAudience) => {
const redundancyObject = redundancyModel.toActivityPubObject()
const url = getUpdateActivityPubUrl(redundancyModel.url, redundancyModel.updatedAt.toISOString())
const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, undefined)
const audience = getObjectFollowersAudience(accountsInvolvedInVideo)
return buildUpdateActivity(url, byActor, redundancyObject, audience)
}
const updateActivity = buildUpdateActivity(url, byActor, redundancyObject, audience)
return unicastTo(updateActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
return sendVideoRelatedActivity(activityBuilder, { byActor, video })
}
// ---------------------------------------------------------------------------

View File

@ -1,13 +1,36 @@
import { Transaction } from 'sequelize'
import { Activity } from '../../../../shared/models/activitypub'
import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
import { logger } from '../../../helpers/logger'
import { ActorModel } from '../../../models/activitypub/actor'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { JobQueue } from '../../job-queue'
import { VideoModel } from '../../../models/video/video'
import { getActorsInvolvedInVideo } from '../audience'
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
import { getServerActor } from '../../../helpers/utils'
async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
byActor: ActorModel,
video: VideoModel,
transaction?: Transaction
}) {
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(options.video, options.transaction)
// Send to origin
if (options.video.isOwned() === false) {
const audience = getRemoteVideoAudience(options.video, actorsInvolvedInVideo)
const activity = activityBuilder(audience)
return unicastTo(activity, options.byActor, options.video.VideoChannel.Account.Actor.sharedInboxUrl)
}
// Send to followers
const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
const activity = activityBuilder(audience)
const actorsException = [ options.byActor ]
return broadcastToFollowers(activity, options.byActor, actorsInvolvedInVideo, options.transaction, actorsException)
}
async function forwardVideoRelatedActivity (
activity: Activity,
t: Transaction,
@ -110,7 +133,8 @@ export {
unicastTo,
forwardActivity,
broadcastToActors,
forwardVideoRelatedActivity
forwardVideoRelatedActivity,
sendVideoRelatedActivity
}
// ---------------------------------------------------------------------------

View File

@ -65,7 +65,6 @@ describe('Test stats (excluding redundancy)', function () {
expect(data.totalVideos).to.equal(1)
expect(data.totalInstanceFollowers).to.equal(2)
expect(data.totalInstanceFollowing).to.equal(1)
expect(data.videosRedundancy).to.have.lengthOf(0)
})
it('Should have the correct stats on instance 2', async function () {
@ -80,7 +79,6 @@ describe('Test stats (excluding redundancy)', function () {
expect(data.totalVideos).to.equal(1)
expect(data.totalInstanceFollowers).to.equal(1)
expect(data.totalInstanceFollowing).to.equal(1)
expect(data.videosRedundancy).to.have.lengthOf(0)
})
it('Should have the correct stats on instance 3', async function () {
@ -95,7 +93,6 @@ describe('Test stats (excluding redundancy)', function () {
expect(data.totalVideos).to.equal(1)
expect(data.totalInstanceFollowing).to.equal(1)
expect(data.totalInstanceFollowers).to.equal(0)
expect(data.videosRedundancy).to.have.lengthOf(0)
})
after(async function () {