From dd0ebb715123dfa126a82d4e4fe3a04064ae77b8 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 9 Jul 2021 16:23:01 +0200 Subject: Introduce notifications command --- shared/extra-utils/search/search-command.ts | 2 +- shared/extra-utils/server/servers.ts | 6 +- shared/extra-utils/shared/abstract-command.ts | 7 +- shared/extra-utils/users/index.ts | 3 +- shared/extra-utils/users/notifications-command.ts | 87 +++ shared/extra-utils/users/notifications.ts | 729 +++++++++++++++++++ shared/extra-utils/users/user-notifications.ts | 808 --------------------- .../videos/streaming-playlists-command.ts | 2 +- 8 files changed, 828 insertions(+), 816 deletions(-) create mode 100644 shared/extra-utils/users/notifications-command.ts create mode 100644 shared/extra-utils/users/notifications.ts delete mode 100644 shared/extra-utils/users/user-notifications.ts (limited to 'shared') diff --git a/shared/extra-utils/search/search-command.ts b/shared/extra-utils/search/search-command.ts index 7539a21ec..b6054f093 100644 --- a/shared/extra-utils/search/search-command.ts +++ b/shared/extra-utils/search/search-command.ts @@ -90,7 +90,7 @@ export class SearchCommand extends AbstractCommand { ...options, path, - query: search, + query: { sort: '-publishedAt', ...search }, implicitToken: false, defaultExpectedStatus: HttpStatusCode.OK_200 }) diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts index bd3be8373..e0e49d2c4 100644 --- a/shared/extra-utils/server/servers.ts +++ b/shared/extra-utils/server/servers.ts @@ -11,13 +11,14 @@ import { CLICommand } from '../cli' import { CustomPagesCommand } from '../custom-pages' import { FeedCommand } from '../feeds' import { LogsCommand } from '../logs' +import { SQLCommand } from '../miscs' import { buildServerDirectory, getFileSize, isGithubCI, root, wait } from '../miscs/miscs' import { AbusesCommand } from '../moderation' import { OverviewsCommand } from '../overviews' import { makeGetRequest } from '../requests/requests' import { SearchCommand } from '../search' import { SocketIOCommand } from '../socket' -import { AccountsCommand, BlocklistCommand, SubscriptionsCommand } from '../users' +import { AccountsCommand, BlocklistCommand, NotificationsCommand, SubscriptionsCommand } from '../users' import { BlacklistCommand, CaptionsCommand, @@ -30,7 +31,6 @@ import { ServicesCommand, StreamingPlaylistsCommand } from '../videos' -import { SQLCommand } from '../miscs' import { CommentsCommand } from '../videos/comments-command' import { ConfigCommand } from './config-command' import { ContactFormCommand } from './contact-form-command' @@ -125,6 +125,7 @@ interface ServerInfo { channelsCommand?: ChannelsCommand commentsCommand?: CommentsCommand sqlCommand?: SQLCommand + notificationsCommand?: NotificationsCommand } function parallelTests () { @@ -370,6 +371,7 @@ function assignCommands (server: ServerInfo) { server.channelsCommand = new ChannelsCommand(server) server.commentsCommand = new CommentsCommand(server) server.sqlCommand = new SQLCommand(server) + server.notificationsCommand = new NotificationsCommand(server) } async function reRunServer (server: ServerInfo, configOverride?: any) { diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts index 200db90d4..fd2deb57e 100644 --- a/shared/extra-utils/shared/abstract-command.ts +++ b/shared/extra-utils/shared/abstract-command.ts @@ -78,7 +78,7 @@ abstract class AbstractCommand { } protected getRequest (options: InternalGetCommandOptions) { - const { redirects, query, contentType, accept } = options + const { redirects, query, contentType, accept, range } = options return makeGetRequest({ ...this.buildCommonRequestOptions(options), @@ -86,6 +86,7 @@ abstract class AbstractCommand { redirects, query, contentType, + range, accept }) } @@ -168,10 +169,10 @@ abstract class AbstractCommand { } private buildCommonRequestOptions (options: InternalCommonCommandOptions) { - const { path } = options + const { url, path } = options return { - url: this.server.url, + url: url ?? this.server.url, path, token: this.buildCommonRequestToken(options), diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts index 9f760d7fd..ed166c756 100644 --- a/shared/extra-utils/users/index.ts +++ b/shared/extra-utils/users/index.ts @@ -2,6 +2,7 @@ export * from './accounts-command' export * from './accounts' export * from './blocklist-command' export * from './login' -export * from './user-notifications' +export * from './notifications' +export * from './notifications-command' export * from './subscriptions-command' export * from './users' diff --git a/shared/extra-utils/users/notifications-command.ts b/shared/extra-utils/users/notifications-command.ts new file mode 100644 index 000000000..dfe574ca1 --- /dev/null +++ b/shared/extra-utils/users/notifications-command.ts @@ -0,0 +1,87 @@ +import { ResultList } from '@shared/models' +import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes' +import { UserNotification, UserNotificationSetting } from '../../models/users' +import { AbstractCommand, OverrideCommandOptions } from '../shared' + +export class NotificationsCommand extends AbstractCommand { + + updateMySettings (options: OverrideCommandOptions & { + settings: UserNotificationSetting + }) { + const path = '/api/v1/users/me/notification-settings' + + return this.putBodyRequest({ + ...options, + + path, + fields: options.settings, + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + } + + list (options: OverrideCommandOptions & { + start?: number + count?: number + unread?: boolean + sort?: string + }) { + const { start, count, unread, sort = '-createdAt' } = options + const path = '/api/v1/users/me/notifications' + + return this.getRequestBody>({ + ...options, + + path, + query: { + start, + count, + sort, + unread + }, + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + markAsRead (options: OverrideCommandOptions & { + ids: number[] + }) { + const { ids } = options + const path = '/api/v1/users/me/notifications/read' + + return this.postBodyRequest({ + ...options, + + path, + fields: { ids }, + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + } + + markAsReadAll (options: OverrideCommandOptions) { + const path = '/api/v1/users/me/notifications/read-all' + + return this.postBodyRequest({ + ...options, + + path, + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + } + + async getLastest (options: OverrideCommandOptions = {}) { + const { total, data } = await this.list({ + ...options, + start: 0, + count: 1, + sort: '-createdAt' + }) + + if (total === 0) return undefined + + return data[0] + } +} diff --git a/shared/extra-utils/users/notifications.ts b/shared/extra-utils/users/notifications.ts new file mode 100644 index 000000000..81f0729fa --- /dev/null +++ b/shared/extra-utils/users/notifications.ts @@ -0,0 +1,729 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ + +import { expect } from 'chai' +import { inspect } from 'util' +import { AbuseState, PluginType } from '@shared/models' +import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users' +import { MockSmtpServer } from '../mock-servers/mock-email' +import { doubleFollow } from '../server/follows' +import { flushAndRunMultipleServers, ServerInfo } from '../server/servers' +import { setAccessTokensToServers, userLogin } from './login' +import { createUser, getMyUserInformation } from './users' + +function getAllNotificationsSettings (): UserNotificationSetting { + return { + newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, + newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL + } +} + +type CheckerBaseParams = { + server: ServerInfo + emails: any[] + socketNotifications: UserNotification[] + token: string + check?: { web: boolean, mail: boolean } +} + +type CheckerType = 'presence' | 'absence' + +async function checkNotification ( + base: CheckerBaseParams, + notificationChecker: (notification: UserNotification, type: CheckerType) => void, + emailNotificationFinder: (email: object) => boolean, + checkType: CheckerType +) { + const check = base.check || { web: true, mail: true } + + if (check.web) { + const notification = await base.server.notificationsCommand.getLastest({ token: base.token }) + + if (notification || checkType !== 'absence') { + notificationChecker(notification, checkType) + } + + const socketNotification = base.socketNotifications.find(n => { + try { + notificationChecker(n, 'presence') + return true + } catch { + return false + } + }) + + if (checkType === 'presence') { + const obj = inspect(base.socketNotifications, { depth: 5 }) + expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined + } else { + const obj = inspect(socketNotification, { depth: 5 }) + expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined + } + } + + if (check.mail) { + // Last email + const email = base.emails + .slice() + .reverse() + .find(e => emailNotificationFinder(e)) + + if (checkType === 'presence') { + const emails = base.emails.map(e => e.text) + expect(email, 'The email is absent when is should be present. ' + inspect(emails)).to.not.be.undefined + } else { + expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined + } + } +} + +function checkVideo (video: any, videoName?: string, videoUUID?: string) { + if (videoName) { + expect(video.name).to.be.a('string') + expect(video.name).to.not.be.empty + expect(video.name).to.equal(videoName) + } + + if (videoUUID) { + expect(video.uuid).to.be.a('string') + expect(video.uuid).to.not.be.empty + expect(video.uuid).to.equal(videoUUID) + } + + expect(video.id).to.be.a('number') +} + +function checkActor (actor: any) { + expect(actor.displayName).to.be.a('string') + expect(actor.displayName).to.not.be.empty + expect(actor.host).to.not.be.undefined +} + +function checkComment (comment: any, commentId: number, threadId: number) { + expect(comment.id).to.equal(commentId) + expect(comment.threadId).to.equal(threadId) +} + +async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + checkVideo(notification.video, videoName, videoUUID) + checkActor(notification.video.channel) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { + const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + checkVideo(notification.video, videoName, videoUUID) + checkActor(notification.video.channel) + } else { + expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) + } + } + + function emailNotificationFinder (email: object) { + const text: string = email['text'] + return text.includes(videoUUID) && text.includes('Your video') + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkMyVideoImportIsFinished ( + base: CheckerBaseParams, + videoName: string, + videoUUID: string, + url: string, + success: boolean, + type: CheckerType +) { + const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.videoImport.targetUrl).to.equal(url) + + if (success) checkVideo(notification.videoImport.video, videoName, videoUUID) + } else { + expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url) + } + } + + function emailNotificationFinder (email: object) { + const text: string = email['text'] + const toFind = success ? ' finished' : ' error' + + return text.includes(url) && text.includes(toFind) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_USER_REGISTRATION + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + checkActor(notification.account) + expect(notification.account.name).to.equal(username) + } else { + expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username) + } + } + + function emailNotificationFinder (email: object) { + const text: string = email['text'] + + return text.includes(' registered.') && text.includes(username) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkNewActorFollow ( + base: CheckerBaseParams, + followType: 'channel' | 'account', + followerName: string, + followerDisplayName: string, + followingDisplayName: string, + type: CheckerType +) { + const notificationType = UserNotificationType.NEW_FOLLOW + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + checkActor(notification.actorFollow.follower) + expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName) + expect(notification.actorFollow.follower.name).to.equal(followerName) + expect(notification.actorFollow.follower.host).to.not.be.undefined + + const following = notification.actorFollow.following + expect(following.displayName).to.equal(followingDisplayName) + expect(following.type).to.equal(followType) + } else { + expect(notification).to.satisfy(n => { + return n.type !== notificationType || + (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName) + }) + } + } + + function emailNotificationFinder (email: object) { + const text: string = email['text'] + + return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + checkActor(notification.actorFollow.follower) + expect(notification.actorFollow.follower.name).to.equal('peertube') + expect(notification.actorFollow.follower.host).to.equal(followerHost) + + expect(notification.actorFollow.following.name).to.equal('peertube') + } else { + expect(notification).to.satisfy(n => { + return n.type !== notificationType || n.actorFollow.follower.host !== followerHost + }) + } + } + + function emailNotificationFinder (email: object) { + const text: string = email['text'] + + return text.includes('instance has a new follower') && text.includes(followerHost) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost: string, followingHost: string, type: CheckerType) { + const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + const following = notification.actorFollow.following + checkActor(following) + expect(following.name).to.equal('peertube') + expect(following.host).to.equal(followingHost) + + expect(notification.actorFollow.follower.name).to.equal('peertube') + expect(notification.actorFollow.follower.host).to.equal(followerHost) + } else { + expect(notification).to.satisfy(n => { + return n.type !== notificationType || n.actorFollow.following.host !== followingHost + }) + } + } + + function emailNotificationFinder (email: object) { + const text: string = email['text'] + + return text.includes(' automatically followed a new instance') && text.includes(followingHost) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkCommentMention ( + base: CheckerBaseParams, + uuid: string, + commentId: number, + threadId: number, + byAccountDisplayName: string, + type: CheckerType +) { + const notificationType = UserNotificationType.COMMENT_MENTION + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + checkComment(notification.comment, commentId, threadId) + checkActor(notification.comment.account) + expect(notification.comment.account.displayName).to.equal(byAccountDisplayName) + + checkVideo(notification.comment.video, undefined, uuid) + } else { + expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId) + } + } + + function emailNotificationFinder (email: object) { + const text: string = email['text'] + + return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +let lastEmailCount = 0 + +async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) { + const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + checkComment(notification.comment, commentId, threadId) + checkActor(notification.comment.account) + checkVideo(notification.comment.video, undefined, uuid) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.comment === undefined || n.comment.id !== commentId + }) + } + } + + const commentUrl = `http://localhost:${base.server.port}/w/${uuid};threadId=${threadId}` + + function emailNotificationFinder (email: object) { + return email['text'].indexOf(commentUrl) !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) + + if (type === 'presence') { + // We cannot detect email duplicates, so check we received another email + expect(base.emails).to.have.length.above(lastEmailCount) + lastEmailCount = base.emails.length + } +} + +async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.abuse.id).to.be.a('number') + checkVideo(notification.abuse.video, videoName, videoUUID) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.abuse === undefined || n.abuse.video.uuid !== videoUUID + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, message: string, toEmail: string, type: CheckerType) { + const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.abuse.id).to.equal(abuseId) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.type !== notificationType || n.abuse === undefined || n.abuse.id !== abuseId + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + const to = email['to'].filter(t => t.address === toEmail) + + return text.indexOf(message) !== -1 && to.length !== 0 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number, state: AbuseState, type: CheckerType) { + const notificationType = UserNotificationType.ABUSE_STATE_CHANGE + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.abuse.id).to.equal(abuseId) + expect(notification.abuse.state).to.equal(state) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.abuse === undefined || n.abuse.id !== abuseId + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + + const contains = state === AbuseState.ACCEPTED + ? ' accepted' + : ' rejected' + + return text.indexOf(contains) !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkNewCommentAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.abuse.id).to.be.a('number') + checkVideo(notification.abuse.comment.video, videoName, videoUUID) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.abuse === undefined || n.abuse.comment.video.uuid !== videoUUID + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displayName: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.abuse.id).to.be.a('number') + expect(notification.abuse.account.displayName).to.equal(displayName) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.abuse === undefined || n.abuse.account.displayName !== displayName + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { + const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.videoBlacklist.video.id).to.be.a('number') + checkVideo(notification.videoBlacklist.video, videoName, videoUUID) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.video === undefined || n.video.uuid !== videoUUID + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + return text.indexOf(videoUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkNewBlacklistOnMyVideo ( + base: CheckerBaseParams, + videoUUID: string, + videoName: string, + blacklistType: 'blacklist' | 'unblacklist' +) { + const notificationType = blacklistType === 'blacklist' + ? UserNotificationType.BLACKLIST_ON_MY_VIDEO + : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO + + function notificationChecker (notification: UserNotification) { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video + + checkVideo(video, videoName, videoUUID) + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1 + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, 'presence') +} + +async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.peertube).to.exist + expect(notification.peertube.latestVersion).to.equal(latestVersion) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.peertube === undefined || n.peertube.latestVersion !== latestVersion + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + + return text.includes(latestVersion) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: PluginType, pluginName: string, type: CheckerType) { + const notificationType = UserNotificationType.NEW_PLUGIN_VERSION + + function notificationChecker (notification: UserNotification, type: CheckerType) { + if (type === 'presence') { + expect(notification).to.not.be.undefined + expect(notification.type).to.equal(notificationType) + + expect(notification.plugin.name).to.equal(pluginName) + expect(notification.plugin.type).to.equal(pluginType) + } else { + expect(notification).to.satisfy((n: UserNotification) => { + return n === undefined || n.plugin === undefined || n.plugin.name !== pluginName + }) + } + } + + function emailNotificationFinder (email: object) { + const text = email['text'] + + return text.includes(pluginName) + } + + await checkNotification(base, notificationChecker, emailNotificationFinder, type) +} + +async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) { + const userNotifications: UserNotification[] = [] + const adminNotifications: UserNotification[] = [] + const adminNotificationsServer2: UserNotification[] = [] + const emails: object[] = [] + + const port = await MockSmtpServer.Instance.collectEmails(emails) + + const overrideConfig = { + smtp: { + hostname: 'localhost', + port + }, + signup: { + limit: 20 + } + } + const servers = await flushAndRunMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg)) + + await setAccessTokensToServers(servers) + + if (serversCount > 1) { + await doubleFollow(servers[0], servers[1]) + } + + const user = { + username: 'user_1', + password: 'super password' + } + await createUser({ + url: servers[0].url, + accessToken: servers[0].accessToken, + username: user.username, + password: user.password, + videoQuota: 10 * 1000 * 1000 + }) + const userAccessToken = await userLogin(servers[0], user) + + await servers[0].notificationsCommand.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() }) + await servers[0].notificationsCommand.updateMySettings({ settings: getAllNotificationsSettings() }) + + if (serversCount > 1) { + await servers[1].notificationsCommand.updateMySettings({ settings: getAllNotificationsSettings() }) + } + + { + const socket = servers[0].socketIOCommand.getUserNotificationSocket({ token: userAccessToken }) + socket.on('new-notification', n => userNotifications.push(n)) + } + { + const socket = servers[0].socketIOCommand.getUserNotificationSocket() + socket.on('new-notification', n => adminNotifications.push(n)) + } + + if (serversCount > 1) { + const socket = servers[1].socketIOCommand.getUserNotificationSocket() + socket.on('new-notification', n => adminNotificationsServer2.push(n)) + } + + const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken) + const channelId = resChannel.body.videoChannels[0].id + + return { + userNotifications, + adminNotifications, + adminNotificationsServer2, + userAccessToken, + emails, + servers, + channelId + } +} + +// --------------------------------------------------------------------------- + +export { + getAllNotificationsSettings, + + CheckerBaseParams, + CheckerType, + checkNotification, + checkMyVideoImportIsFinished, + checkUserRegistered, + checkAutoInstanceFollowing, + checkVideoIsPublished, + checkNewVideoFromSubscription, + checkNewActorFollow, + checkNewCommentOnMyVideo, + checkNewBlacklistOnMyVideo, + checkCommentMention, + checkNewVideoAbuseForModerators, + checkVideoAutoBlacklistForModerators, + checkNewAbuseMessage, + checkAbuseStateChange, + checkNewInstanceFollower, + prepareNotificationsTest, + checkNewCommentAbuseForModerators, + checkNewAccountAbuseForModerators, + checkNewPeerTubeVersion, + checkNewPluginVersion +} diff --git a/shared/extra-utils/users/user-notifications.ts b/shared/extra-utils/users/user-notifications.ts deleted file mode 100644 index 961cfcc0f..000000000 --- a/shared/extra-utils/users/user-notifications.ts +++ /dev/null @@ -1,808 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ - -import { expect } from 'chai' -import { inspect } from 'util' -import { AbuseState, PluginType } from '@shared/models' -import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' -import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users' -import { MockSmtpServer } from '../mock-servers/mock-email' -import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' -import { doubleFollow } from '../server/follows' -import { flushAndRunMultipleServers, ServerInfo } from '../server/servers' -import { setAccessTokensToServers, userLogin } from './login' -import { createUser, getMyUserInformation } from './users' - -function updateMyNotificationSettings ( - url: string, - token: string, - settings: UserNotificationSetting, - statusCodeExpected = HttpStatusCode.NO_CONTENT_204 -) { - const path = '/api/v1/users/me/notification-settings' - - return makePutBodyRequest({ - url, - path, - token, - fields: settings, - statusCodeExpected - }) -} - -async function getUserNotifications ( - url: string, - token: string, - start: number, - count: number, - unread?: boolean, - sort = '-createdAt', - statusCodeExpected = HttpStatusCode.OK_200 -) { - const path = '/api/v1/users/me/notifications' - - return makeGetRequest({ - url, - path, - token, - query: { - start, - count, - sort, - unread - }, - statusCodeExpected - }) -} - -function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = HttpStatusCode.NO_CONTENT_204) { - const path = '/api/v1/users/me/notifications/read' - - return makePostBodyRequest({ - url, - path, - token, - fields: { ids }, - statusCodeExpected - }) -} - -function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) { - const path = '/api/v1/users/me/notifications/read-all' - - return makePostBodyRequest({ - url, - path, - token, - statusCodeExpected - }) -} - -async function getLastNotification (serverUrl: string, accessToken: string) { - const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt') - - if (res.body.total === 0) return undefined - - return res.body.data[0] as UserNotification -} - -type CheckerBaseParams = { - server: ServerInfo - emails: any[] - socketNotifications: UserNotification[] - token: string - check?: { web: boolean, mail: boolean } -} - -type CheckerType = 'presence' | 'absence' - -async function checkNotification ( - base: CheckerBaseParams, - notificationChecker: (notification: UserNotification, type: CheckerType) => void, - emailNotificationFinder: (email: object) => boolean, - checkType: CheckerType -) { - const check = base.check || { web: true, mail: true } - - if (check.web) { - const notification = await getLastNotification(base.server.url, base.token) - - if (notification || checkType !== 'absence') { - notificationChecker(notification, checkType) - } - - const socketNotification = base.socketNotifications.find(n => { - try { - notificationChecker(n, 'presence') - return true - } catch { - return false - } - }) - - if (checkType === 'presence') { - const obj = inspect(base.socketNotifications, { depth: 5 }) - expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined - } else { - const obj = inspect(socketNotification, { depth: 5 }) - expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined - } - } - - if (check.mail) { - // Last email - const email = base.emails - .slice() - .reverse() - .find(e => emailNotificationFinder(e)) - - if (checkType === 'presence') { - const emails = base.emails.map(e => e.text) - expect(email, 'The email is absent when is should be present. ' + inspect(emails)).to.not.be.undefined - } else { - expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined - } - } -} - -function checkVideo (video: any, videoName?: string, videoUUID?: string) { - if (videoName) { - expect(video.name).to.be.a('string') - expect(video.name).to.not.be.empty - expect(video.name).to.equal(videoName) - } - - if (videoUUID) { - expect(video.uuid).to.be.a('string') - expect(video.uuid).to.not.be.empty - expect(video.uuid).to.equal(videoUUID) - } - - expect(video.id).to.be.a('number') -} - -function checkActor (actor: any) { - expect(actor.displayName).to.be.a('string') - expect(actor.displayName).to.not.be.empty - expect(actor.host).to.not.be.undefined -} - -function checkComment (comment: any, commentId: number, threadId: number) { - expect(comment.id).to.equal(commentId) - expect(comment.threadId).to.equal(threadId) -} - -async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkVideo(notification.video, videoName, videoUUID) - checkActor(notification.video.channel) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) { - const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkVideo(notification.video, videoName, videoUUID) - checkActor(notification.video.channel) - } else { - expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - return text.includes(videoUUID) && text.includes('Your video') - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkMyVideoImportIsFinished ( - base: CheckerBaseParams, - videoName: string, - videoUUID: string, - url: string, - success: boolean, - type: CheckerType -) { - const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.videoImport.targetUrl).to.equal(url) - - if (success) checkVideo(notification.videoImport.video, videoName, videoUUID) - } else { - expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - const toFind = success ? ' finished' : ' error' - - return text.includes(url) && text.includes(toFind) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_USER_REGISTRATION - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkActor(notification.account) - expect(notification.account.name).to.equal(username) - } else { - expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(' registered.') && text.includes(username) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkNewActorFollow ( - base: CheckerBaseParams, - followType: 'channel' | 'account', - followerName: string, - followerDisplayName: string, - followingDisplayName: string, - type: CheckerType -) { - const notificationType = UserNotificationType.NEW_FOLLOW - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkActor(notification.actorFollow.follower) - expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName) - expect(notification.actorFollow.follower.name).to.equal(followerName) - expect(notification.actorFollow.follower.host).to.not.be.undefined - - const following = notification.actorFollow.following - expect(following.displayName).to.equal(followingDisplayName) - expect(following.type).to.equal(followType) - } else { - expect(notification).to.satisfy(n => { - return n.type !== notificationType || - (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName) - }) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkActor(notification.actorFollow.follower) - expect(notification.actorFollow.follower.name).to.equal('peertube') - expect(notification.actorFollow.follower.host).to.equal(followerHost) - - expect(notification.actorFollow.following.name).to.equal('peertube') - } else { - expect(notification).to.satisfy(n => { - return n.type !== notificationType || n.actorFollow.follower.host !== followerHost - }) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes('instance has a new follower') && text.includes(followerHost) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost: string, followingHost: string, type: CheckerType) { - const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - const following = notification.actorFollow.following - checkActor(following) - expect(following.name).to.equal('peertube') - expect(following.host).to.equal(followingHost) - - expect(notification.actorFollow.follower.name).to.equal('peertube') - expect(notification.actorFollow.follower.host).to.equal(followerHost) - } else { - expect(notification).to.satisfy(n => { - return n.type !== notificationType || n.actorFollow.following.host !== followingHost - }) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(' automatically followed a new instance') && text.includes(followingHost) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkCommentMention ( - base: CheckerBaseParams, - uuid: string, - commentId: number, - threadId: number, - byAccountDisplayName: string, - type: CheckerType -) { - const notificationType = UserNotificationType.COMMENT_MENTION - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkComment(notification.comment, commentId, threadId) - checkActor(notification.comment.account) - expect(notification.comment.account.displayName).to.equal(byAccountDisplayName) - - checkVideo(notification.comment.video, undefined, uuid) - } else { - expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId) - } - } - - function emailNotificationFinder (email: object) { - const text: string = email['text'] - - return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -let lastEmailCount = 0 - -async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) { - const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - checkComment(notification.comment, commentId, threadId) - checkActor(notification.comment.account) - checkVideo(notification.comment.video, undefined, uuid) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.comment === undefined || n.comment.id !== commentId - }) - } - } - - const commentUrl = `http://localhost:${base.server.port}/w/${uuid};threadId=${threadId}` - - function emailNotificationFinder (email: object) { - return email['text'].indexOf(commentUrl) !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) - - if (type === 'presence') { - // We cannot detect email duplicates, so check we received another email - expect(base.emails).to.have.length.above(lastEmailCount) - lastEmailCount = base.emails.length - } -} - -async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.be.a('number') - checkVideo(notification.abuse.video, videoName, videoUUID) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.video.uuid !== videoUUID - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, message: string, toEmail: string, type: CheckerType) { - const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.equal(abuseId) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.type !== notificationType || n.abuse === undefined || n.abuse.id !== abuseId - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - const to = email['to'].filter(t => t.address === toEmail) - - return text.indexOf(message) !== -1 && to.length !== 0 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number, state: AbuseState, type: CheckerType) { - const notificationType = UserNotificationType.ABUSE_STATE_CHANGE - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.equal(abuseId) - expect(notification.abuse.state).to.equal(state) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.id !== abuseId - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - - const contains = state === AbuseState.ACCEPTED - ? ' accepted' - : ' rejected' - - return text.indexOf(contains) !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkNewCommentAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.be.a('number') - checkVideo(notification.abuse.comment.video, videoName, videoUUID) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.comment.video.uuid !== videoUUID - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displayName: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.abuse.id).to.be.a('number') - expect(notification.abuse.account.displayName).to.equal(displayName) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.abuse === undefined || n.abuse.account.displayName !== displayName - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { - const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.videoBlacklist.video.id).to.be.a('number') - checkVideo(notification.videoBlacklist.video, videoName, videoUUID) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.video === undefined || n.video.uuid !== videoUUID - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(videoUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkNewBlacklistOnMyVideo ( - base: CheckerBaseParams, - videoUUID: string, - videoName: string, - blacklistType: 'blacklist' | 'unblacklist' -) { - const notificationType = blacklistType === 'blacklist' - ? UserNotificationType.BLACKLIST_ON_MY_VIDEO - : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO - - function notificationChecker (notification: UserNotification) { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video - - checkVideo(video, videoName, videoUUID) - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1 - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, 'presence') -} - -async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.peertube).to.exist - expect(notification.peertube.latestVersion).to.equal(latestVersion) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.peertube === undefined || n.peertube.latestVersion !== latestVersion - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - - return text.includes(latestVersion) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: PluginType, pluginName: string, type: CheckerType) { - const notificationType = UserNotificationType.NEW_PLUGIN_VERSION - - function notificationChecker (notification: UserNotification, type: CheckerType) { - if (type === 'presence') { - expect(notification).to.not.be.undefined - expect(notification.type).to.equal(notificationType) - - expect(notification.plugin.name).to.equal(pluginName) - expect(notification.plugin.type).to.equal(pluginType) - } else { - expect(notification).to.satisfy((n: UserNotification) => { - return n === undefined || n.plugin === undefined || n.plugin.name !== pluginName - }) - } - } - - function emailNotificationFinder (email: object) { - const text = email['text'] - - return text.includes(pluginName) - } - - await checkNotification(base, notificationChecker, emailNotificationFinder, type) -} - -function getAllNotificationsSettings (): UserNotificationSetting { - return { - newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, - newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL - } -} - -async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) { - const userNotifications: UserNotification[] = [] - const adminNotifications: UserNotification[] = [] - const adminNotificationsServer2: UserNotification[] = [] - const emails: object[] = [] - - const port = await MockSmtpServer.Instance.collectEmails(emails) - - const overrideConfig = { - smtp: { - hostname: 'localhost', - port - }, - signup: { - limit: 20 - } - } - const servers = await flushAndRunMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg)) - - await setAccessTokensToServers(servers) - - if (serversCount > 1) { - await doubleFollow(servers[0], servers[1]) - } - - const user = { - username: 'user_1', - password: 'super password' - } - await createUser({ - url: servers[0].url, - accessToken: servers[0].accessToken, - username: user.username, - password: user.password, - videoQuota: 10 * 1000 * 1000 - }) - const userAccessToken = await userLogin(servers[0], user) - - await updateMyNotificationSettings(servers[0].url, userAccessToken, getAllNotificationsSettings()) - await updateMyNotificationSettings(servers[0].url, servers[0].accessToken, getAllNotificationsSettings()) - - if (serversCount > 1) { - await updateMyNotificationSettings(servers[1].url, servers[1].accessToken, getAllNotificationsSettings()) - } - - { - const socket = servers[0].socketIOCommand.getUserNotificationSocket({ token: userAccessToken }) - socket.on('new-notification', n => userNotifications.push(n)) - } - { - const socket = servers[0].socketIOCommand.getUserNotificationSocket() - socket.on('new-notification', n => adminNotifications.push(n)) - } - - if (serversCount > 1) { - const socket = servers[1].socketIOCommand.getUserNotificationSocket() - socket.on('new-notification', n => adminNotificationsServer2.push(n)) - } - - const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken) - const channelId = resChannel.body.videoChannels[0].id - - return { - userNotifications, - adminNotifications, - adminNotificationsServer2, - userAccessToken, - emails, - servers, - channelId - } -} - -// --------------------------------------------------------------------------- - -export { - CheckerBaseParams, - CheckerType, - getAllNotificationsSettings, - checkNotification, - markAsReadAllNotifications, - checkMyVideoImportIsFinished, - checkUserRegistered, - checkAutoInstanceFollowing, - checkVideoIsPublished, - checkNewVideoFromSubscription, - checkNewActorFollow, - checkNewCommentOnMyVideo, - checkNewBlacklistOnMyVideo, - checkCommentMention, - updateMyNotificationSettings, - checkNewVideoAbuseForModerators, - checkVideoAutoBlacklistForModerators, - checkNewAbuseMessage, - checkAbuseStateChange, - getUserNotifications, - markAsReadNotifications, - getLastNotification, - checkNewInstanceFollower, - prepareNotificationsTest, - checkNewCommentAbuseForModerators, - checkNewAccountAbuseForModerators, - checkNewPeerTubeVersion, - checkNewPluginVersion -} diff --git a/shared/extra-utils/videos/streaming-playlists-command.ts b/shared/extra-utils/videos/streaming-playlists-command.ts index b109597c9..fab3eb556 100644 --- a/shared/extra-utils/videos/streaming-playlists-command.ts +++ b/shared/extra-utils/videos/streaming-playlists-command.ts @@ -21,7 +21,7 @@ export class StreamingPlaylistsCommand extends AbstractCommand { url: string range?: string }) { - return unwrapText(this.getRawRequest({ + return unwrapBody(this.getRawRequest({ ...options, url: options.url, -- cgit v1.2.3