Remove tmp file on image processing error
[oweals/peertube.git] / shared / utils / users / user-notifications.ts
1 /* tslint:disable:no-unused-expression */
2
3 import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
4 import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users'
5 import { ServerInfo } from '..'
6 import { expect } from 'chai'
7 import { inspect } from 'util'
8
9 function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) {
10   const path = '/api/v1/users/me/notification-settings'
11
12   return makePutBodyRequest({
13     url,
14     path,
15     token,
16     fields: settings,
17     statusCodeExpected
18   })
19 }
20
21 async function getUserNotifications (
22   url: string,
23   token: string,
24   start: number,
25   count: number,
26   unread?: boolean,
27   sort = '-createdAt',
28   statusCodeExpected = 200
29 ) {
30   const path = '/api/v1/users/me/notifications'
31
32   return makeGetRequest({
33     url,
34     path,
35     token,
36     query: {
37       start,
38       count,
39       sort,
40       unread
41     },
42     statusCodeExpected
43   })
44 }
45
46 function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = 204) {
47   const path = '/api/v1/users/me/notifications/read'
48
49   return makePostBodyRequest({
50     url,
51     path,
52     token,
53     fields: { ids },
54     statusCodeExpected
55   })
56 }
57 function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = 204) {
58   const path = '/api/v1/users/me/notifications/read-all'
59
60   return makePostBodyRequest({
61     url,
62     path,
63     token,
64     statusCodeExpected
65   })
66 }
67
68 async function getLastNotification (serverUrl: string, accessToken: string) {
69   const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
70
71   if (res.body.total === 0) return undefined
72
73   return res.body.data[0] as UserNotification
74 }
75
76 type CheckerBaseParams = {
77   server: ServerInfo
78   emails: object[]
79   socketNotifications: UserNotification[]
80   token: string,
81   check?: { web: boolean, mail: boolean }
82 }
83
84 type CheckerType = 'presence' | 'absence'
85
86 async function checkNotification (
87   base: CheckerBaseParams,
88   notificationChecker: (notification: UserNotification, type: CheckerType) => void,
89   emailNotificationFinder: (email: object) => boolean,
90   checkType: CheckerType
91 ) {
92   const check = base.check || { web: true, mail: true }
93
94   if (check.web) {
95     const notification = await getLastNotification(base.server.url, base.token)
96
97     if (notification || checkType !== 'absence') {
98       notificationChecker(notification, checkType)
99     }
100
101     const socketNotification = base.socketNotifications.find(n => {
102       try {
103         notificationChecker(n, 'presence')
104         return true
105       } catch {
106         return false
107       }
108     })
109
110     if (checkType === 'presence') {
111       const obj = inspect(base.socketNotifications, { depth: 5 })
112       expect(socketNotification, 'The socket notification is absent. ' + obj).to.not.be.undefined
113     } else {
114       const obj = inspect(socketNotification, { depth: 5 })
115       expect(socketNotification, 'The socket notification is present. ' + obj).to.be.undefined
116     }
117   }
118
119   if (check.mail) {
120     // Last email
121     const email = base.emails
122                       .slice()
123                       .reverse()
124                       .find(e => emailNotificationFinder(e))
125
126     if (checkType === 'presence') {
127       expect(email, 'The email is absent. ' + inspect(base.emails)).to.not.be.undefined
128     } else {
129       expect(email, 'The email is present. ' + inspect(email)).to.be.undefined
130     }
131   }
132 }
133
134 function checkVideo (video: any, videoName?: string, videoUUID?: string) {
135   expect(video.name).to.be.a('string')
136   expect(video.name).to.not.be.empty
137   if (videoName) expect(video.name).to.equal(videoName)
138
139   expect(video.uuid).to.be.a('string')
140   expect(video.uuid).to.not.be.empty
141   if (videoUUID) expect(video.uuid).to.equal(videoUUID)
142
143   expect(video.id).to.be.a('number')
144 }
145
146 function checkActor (actor: any) {
147   expect(actor.displayName).to.be.a('string')
148   expect(actor.displayName).to.not.be.empty
149   expect(actor.host).to.not.be.undefined
150 }
151
152 function checkComment (comment: any, commentId: number, threadId: number) {
153   expect(comment.id).to.equal(commentId)
154   expect(comment.threadId).to.equal(threadId)
155 }
156
157 async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
158   const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
159
160   function notificationChecker (notification: UserNotification, type: CheckerType) {
161     if (type === 'presence') {
162       expect(notification).to.not.be.undefined
163       expect(notification.type).to.equal(notificationType)
164
165       checkVideo(notification.video, videoName, videoUUID)
166       checkActor(notification.video.channel)
167     } else {
168       expect(notification).to.satisfy((n: UserNotification) => {
169         return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName
170       })
171     }
172   }
173
174   function emailFinder (email: object) {
175     const text = email[ 'text' ]
176     return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1
177   }
178
179   await checkNotification(base, notificationChecker, emailFinder, type)
180 }
181
182 async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
183   const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
184
185   function notificationChecker (notification: UserNotification, type: CheckerType) {
186     if (type === 'presence') {
187       expect(notification).to.not.be.undefined
188       expect(notification.type).to.equal(notificationType)
189
190       checkVideo(notification.video, videoName, videoUUID)
191       checkActor(notification.video.channel)
192     } else {
193       expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
194     }
195   }
196
197   function emailFinder (email: object) {
198     const text: string = email[ 'text' ]
199     return text.includes(videoUUID) && text.includes('Your video')
200   }
201
202   await checkNotification(base, notificationChecker, emailFinder, type)
203 }
204
205 async function checkMyVideoImportIsFinished (
206   base: CheckerBaseParams,
207   videoName: string,
208   videoUUID: string,
209   url: string,
210   success: boolean,
211   type: CheckerType
212 ) {
213   const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
214
215   function notificationChecker (notification: UserNotification, type: CheckerType) {
216     if (type === 'presence') {
217       expect(notification).to.not.be.undefined
218       expect(notification.type).to.equal(notificationType)
219
220       expect(notification.videoImport.targetUrl).to.equal(url)
221
222       if (success) checkVideo(notification.videoImport.video, videoName, videoUUID)
223     } else {
224       expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
225     }
226   }
227
228   function emailFinder (email: object) {
229     const text: string = email[ 'text' ]
230     const toFind = success ? ' finished' : ' error'
231
232     return text.includes(url) && text.includes(toFind)
233   }
234
235   await checkNotification(base, notificationChecker, emailFinder, type)
236 }
237
238 async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) {
239   const notificationType = UserNotificationType.NEW_USER_REGISTRATION
240
241   function notificationChecker (notification: UserNotification, type: CheckerType) {
242     if (type === 'presence') {
243       expect(notification).to.not.be.undefined
244       expect(notification.type).to.equal(notificationType)
245
246       checkActor(notification.account)
247       expect(notification.account.name).to.equal(username)
248     } else {
249       expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username)
250     }
251   }
252
253   function emailFinder (email: object) {
254     const text: string = email[ 'text' ]
255
256     return text.includes(' registered ') && text.includes(username)
257   }
258
259   await checkNotification(base, notificationChecker, emailFinder, type)
260 }
261
262 async function checkNewActorFollow (
263   base: CheckerBaseParams,
264   followType: 'channel' | 'account',
265   followerName: string,
266   followerDisplayName: string,
267   followingDisplayName: string,
268   type: CheckerType
269 ) {
270   const notificationType = UserNotificationType.NEW_FOLLOW
271
272   function notificationChecker (notification: UserNotification, type: CheckerType) {
273     if (type === 'presence') {
274       expect(notification).to.not.be.undefined
275       expect(notification.type).to.equal(notificationType)
276
277       checkActor(notification.actorFollow.follower)
278       expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
279       expect(notification.actorFollow.follower.name).to.equal(followerName)
280       expect(notification.actorFollow.follower.host).to.not.be.undefined
281
282       expect(notification.actorFollow.following.displayName).to.equal(followingDisplayName)
283       expect(notification.actorFollow.following.type).to.equal(followType)
284     } else {
285       expect(notification).to.satisfy(n => {
286         return n.type !== notificationType ||
287           (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
288       })
289     }
290   }
291
292   function emailFinder (email: object) {
293     const text: string = email[ 'text' ]
294
295     return text.includes('Your ' + followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
296   }
297
298   await checkNotification(base, notificationChecker, emailFinder, type)
299 }
300
301 async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) {
302   const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
303
304   function notificationChecker (notification: UserNotification, type: CheckerType) {
305     if (type === 'presence') {
306       expect(notification).to.not.be.undefined
307       expect(notification.type).to.equal(notificationType)
308
309       checkActor(notification.actorFollow.follower)
310       expect(notification.actorFollow.follower.name).to.equal('peertube')
311       expect(notification.actorFollow.follower.host).to.equal(followerHost)
312
313       expect(notification.actorFollow.following.name).to.equal('peertube')
314     } else {
315       expect(notification).to.satisfy(n => {
316         return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
317       })
318     }
319   }
320
321   function emailFinder (email: object) {
322     const text: string = email[ 'text' ]
323
324     return text.includes('instance has a new follower') && text.includes(followerHost)
325   }
326
327   await checkNotification(base, notificationChecker, emailFinder, type)
328 }
329
330 async function checkCommentMention (
331   base: CheckerBaseParams,
332   uuid: string,
333   commentId: number,
334   threadId: number,
335   byAccountDisplayName: string,
336   type: CheckerType
337 ) {
338   const notificationType = UserNotificationType.COMMENT_MENTION
339
340   function notificationChecker (notification: UserNotification, type: CheckerType) {
341     if (type === 'presence') {
342       expect(notification).to.not.be.undefined
343       expect(notification.type).to.equal(notificationType)
344
345       checkComment(notification.comment, commentId, threadId)
346       checkActor(notification.comment.account)
347       expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
348
349       checkVideo(notification.comment.video, undefined, uuid)
350     } else {
351       expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
352     }
353   }
354
355   function emailFinder (email: object) {
356     const text: string = email[ 'text' ]
357
358     return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName)
359   }
360
361   await checkNotification(base, notificationChecker, emailFinder, type)
362 }
363
364 let lastEmailCount = 0
365 async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
366   const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
367
368   function notificationChecker (notification: UserNotification, type: CheckerType) {
369     if (type === 'presence') {
370       expect(notification).to.not.be.undefined
371       expect(notification.type).to.equal(notificationType)
372
373       checkComment(notification.comment, commentId, threadId)
374       checkActor(notification.comment.account)
375       checkVideo(notification.comment.video, undefined, uuid)
376     } else {
377       expect(notification).to.satisfy((n: UserNotification) => {
378         return n === undefined || n.comment === undefined || n.comment.id !== commentId
379       })
380     }
381   }
382
383   const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}`
384   function emailFinder (email: object) {
385     return email[ 'text' ].indexOf(commentUrl) !== -1
386   }
387
388   await checkNotification(base, notificationChecker, emailFinder, type)
389
390   if (type === 'presence') {
391     // We cannot detect email duplicates, so check we received another email
392     expect(base.emails).to.have.length.above(lastEmailCount)
393     lastEmailCount = base.emails.length
394   }
395 }
396
397 async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
398   const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS
399
400   function notificationChecker (notification: UserNotification, type: CheckerType) {
401     if (type === 'presence') {
402       expect(notification).to.not.be.undefined
403       expect(notification.type).to.equal(notificationType)
404
405       expect(notification.videoAbuse.id).to.be.a('number')
406       checkVideo(notification.videoAbuse.video, videoName, videoUUID)
407     } else {
408       expect(notification).to.satisfy((n: UserNotification) => {
409         return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID
410       })
411     }
412   }
413
414   function emailFinder (email: object) {
415     const text = email[ 'text' ]
416     return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
417   }
418
419   await checkNotification(base, notificationChecker, emailFinder, type)
420 }
421
422 async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
423   const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
424
425   function notificationChecker (notification: UserNotification, type: CheckerType) {
426     if (type === 'presence') {
427       expect(notification).to.not.be.undefined
428       expect(notification.type).to.equal(notificationType)
429
430       expect(notification.video.id).to.be.a('number')
431       checkVideo(notification.video, videoName, videoUUID)
432     } else {
433       expect(notification).to.satisfy((n: UserNotification) => {
434         return n === undefined || n.video === undefined || n.video.uuid !== videoUUID
435       })
436     }
437   }
438
439   function emailFinder (email: object) {
440     const text = email[ 'text' ]
441     return text.indexOf(videoUUID) !== -1 && email[ 'text' ].indexOf('video-auto-blacklist/list') !== -1
442   }
443
444   await checkNotification(base, notificationChecker, emailFinder, type)
445 }
446
447 async function checkNewBlacklistOnMyVideo (
448   base: CheckerBaseParams,
449   videoUUID: string,
450   videoName: string,
451   blacklistType: 'blacklist' | 'unblacklist'
452 ) {
453   const notificationType = blacklistType === 'blacklist'
454     ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
455     : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
456
457   function notificationChecker (notification: UserNotification) {
458     expect(notification).to.not.be.undefined
459     expect(notification.type).to.equal(notificationType)
460
461     const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
462
463     checkVideo(video, videoName, videoUUID)
464   }
465
466   function emailFinder (email: object) {
467     const text = email[ 'text' ]
468     return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
469   }
470
471   await checkNotification(base, notificationChecker, emailFinder, 'presence')
472 }
473
474 // ---------------------------------------------------------------------------
475
476 export {
477   CheckerBaseParams,
478   CheckerType,
479   checkNotification,
480   markAsReadAllNotifications,
481   checkMyVideoImportIsFinished,
482   checkUserRegistered,
483   checkVideoIsPublished,
484   checkNewVideoFromSubscription,
485   checkNewActorFollow,
486   checkNewCommentOnMyVideo,
487   checkNewBlacklistOnMyVideo,
488   checkCommentMention,
489   updateMyNotificationSettings,
490   checkNewVideoAbuseForModerators,
491   checkVideoAutoBlacklistForModerators,
492   getUserNotifications,
493   markAsReadNotifications,
494   getLastNotification,
495   checkNewInstanceFollower
496 }