Add notification settings migration
[oweals/peertube.git] / server / tests / api / users / user-notifications.ts
1 /* tslint:disable:no-unused-expression */
2
3 import * as chai from 'chai'
4 import 'mocha'
5 import {
6   addVideoToBlacklist,
7   createUser,
8   doubleFollow,
9   flushAndRunMultipleServers,
10   flushTests,
11   getMyUserInformation,
12   immutableAssign,
13   removeVideoFromBlacklist,
14   reportVideoAbuse,
15   updateVideo,
16   userLogin,
17   wait
18 } from '../../../../shared/utils'
19 import { killallServers, ServerInfo, uploadVideo } from '../../../../shared/utils/index'
20 import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
21 import { waitJobs } from '../../../../shared/utils/server/jobs'
22 import { getUserNotificationSocket } from '../../../../shared/utils/socket/socket-io'
23 import {
24   CheckerBaseParams,
25   checkNewBlacklistOnMyVideo,
26   checkNewCommentOnMyVideo,
27   checkNewVideoAbuseForModerators,
28   checkNewVideoFromSubscription,
29   getLastNotification,
30   getUserNotifications,
31   markAsReadNotifications,
32   updateMyNotificationSettings
33 } from '../../../../shared/utils/users/user-notifications'
34 import { User, UserNotification, UserNotificationSettingValue } from '../../../../shared/models/users'
35 import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
36 import { addUserSubscription } from '../../../../shared/utils/users/user-subscriptions'
37 import { VideoPrivacy } from '../../../../shared/models/videos'
38 import { getYoutubeVideoUrl, importVideo } from '../../../../shared/utils/videos/video-imports'
39 import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments'
40
41 const expect = chai.expect
42
43 async function uploadVideoByRemoteAccount (servers: ServerInfo[], videoNameId: number, additionalParams: any = {}) {
44   const data = Object.assign({ name: 'remote video ' + videoNameId }, additionalParams)
45   const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, data)
46
47   await waitJobs(servers)
48
49   return res.body.video.uuid
50 }
51
52 async function uploadVideoByLocalAccount (servers: ServerInfo[], videoNameId: number, additionalParams: any = {}) {
53   const data = Object.assign({ name: 'local video ' + videoNameId }, additionalParams)
54   const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, data)
55
56   await waitJobs(servers)
57
58   return res.body.video.uuid
59 }
60
61 describe('Test users notifications', function () {
62   let servers: ServerInfo[] = []
63   let userAccessToken: string
64   let userNotifications: UserNotification[] = []
65   let adminNotifications: UserNotification[] = []
66   const emails: object[] = []
67
68   before(async function () {
69     this.timeout(120000)
70
71     await MockSmtpServer.Instance.collectEmails(emails)
72
73     await flushTests()
74
75     const overrideConfig = {
76       smtp: {
77         hostname: 'localhost'
78       }
79     }
80     servers = await flushAndRunMultipleServers(2, overrideConfig)
81
82     // Get the access tokens
83     await setAccessTokensToServers(servers)
84
85     // Server 1 and server 2 follow each other
86     await doubleFollow(servers[0], servers[1])
87
88     await waitJobs(servers)
89
90     const user = {
91       username: 'user_1',
92       password: 'super password'
93     }
94     await createUser(servers[0].url, servers[0].accessToken, user.username, user.password, 10 * 1000 * 1000)
95     userAccessToken = await userLogin(servers[0], user)
96
97     await updateMyNotificationSettings(servers[0].url, userAccessToken, {
98       newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
99       newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
100       blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
101       videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
102     })
103
104     {
105       const socket = getUserNotificationSocket(servers[ 0 ].url, userAccessToken)
106       socket.on('new-notification', n => userNotifications.push(n))
107     }
108     {
109       const socket = getUserNotificationSocket(servers[ 0 ].url, servers[0].accessToken)
110       socket.on('new-notification', n => adminNotifications.push(n))
111     }
112   })
113
114   describe('New video from my subscription notification', function () {
115     let baseParams: CheckerBaseParams
116
117     before(() => {
118       baseParams = {
119         server: servers[0],
120         emails,
121         socketNotifications: userNotifications,
122         token: userAccessToken
123       }
124     })
125
126     it('Should not send notifications if the user does not follow the video publisher', async function () {
127       await uploadVideoByLocalAccount(servers, 1)
128
129       const notification = await getLastNotification(servers[ 0 ].url, userAccessToken)
130       expect(notification).to.be.undefined
131
132       expect(emails).to.have.lengthOf(0)
133       expect(userNotifications).to.have.lengthOf(0)
134     })
135
136     it('Should send a new video notification if the user follows the local video publisher', async function () {
137       await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001')
138
139       const videoNameId = 10
140       const videoName = 'local video ' + videoNameId
141
142       const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
143       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
144     })
145
146     it('Should send a new video notification from a remote account', async function () {
147       this.timeout(50000) // Server 2 has transcoding enabled
148
149       await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002')
150
151       const videoNameId = 20
152       const videoName = 'remote video ' + videoNameId
153
154       const uuid = await uploadVideoByRemoteAccount(servers, videoNameId)
155       await waitJobs(servers)
156
157       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
158     })
159
160     it('Should send a new video notification on a scheduled publication', async function () {
161       this.timeout(20000)
162
163       const videoNameId = 30
164       const videoName = 'local video ' + videoNameId
165
166       // In 2 seconds
167       let updateAt = new Date(new Date().getTime() + 2000)
168
169       const data = {
170         privacy: VideoPrivacy.PRIVATE,
171         scheduleUpdate: {
172           updateAt: updateAt.toISOString(),
173           privacy: VideoPrivacy.PUBLIC
174         }
175       }
176       const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
177
178       await wait(6000)
179       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
180     })
181
182     it('Should send a new video notification on a remote scheduled publication', async function () {
183       this.timeout(20000)
184
185       const videoNameId = 40
186       const videoName = 'remote video ' + videoNameId
187
188       // In 2 seconds
189       let updateAt = new Date(new Date().getTime() + 2000)
190
191       const data = {
192         privacy: VideoPrivacy.PRIVATE,
193         scheduleUpdate: {
194           updateAt: updateAt.toISOString(),
195           privacy: VideoPrivacy.PUBLIC
196         }
197       }
198       const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
199       await waitJobs(servers)
200
201       await wait(6000)
202       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
203     })
204
205     it('Should not send a notification before the video is published', async function () {
206       this.timeout(20000)
207
208       const videoNameId = 50
209       const videoName = 'local video ' + videoNameId
210
211       let updateAt = new Date(new Date().getTime() + 100000)
212
213       const data = {
214         privacy: VideoPrivacy.PRIVATE,
215         scheduleUpdate: {
216           updateAt: updateAt.toISOString(),
217           privacy: VideoPrivacy.PUBLIC
218         }
219       }
220       const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
221
222       await wait(6000)
223       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
224     })
225
226     it('Should send a new video notification when a video becomes public', async function () {
227       this.timeout(10000)
228
229       const videoNameId = 60
230       const videoName = 'local video ' + videoNameId
231
232       const data = { privacy: VideoPrivacy.PRIVATE }
233       const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
234
235       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
236
237       await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC })
238
239       await wait(500)
240       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
241     })
242
243     it('Should send a new video notification when a remote video becomes public', async function () {
244       this.timeout(20000)
245
246       const videoNameId = 70
247       const videoName = 'remote video ' + videoNameId
248
249       const data = { privacy: VideoPrivacy.PRIVATE }
250       const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
251       await waitJobs(servers)
252
253       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
254
255       await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.PUBLIC })
256
257       await waitJobs(servers)
258       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
259     })
260
261     it('Should not send a new video notification when a video becomes unlisted', async function () {
262       this.timeout(20000)
263
264       const videoNameId = 80
265       const videoName = 'local video ' + videoNameId
266
267       const data = { privacy: VideoPrivacy.PRIVATE }
268       const uuid = await uploadVideoByLocalAccount(servers, videoNameId, data)
269
270       await updateVideo(servers[0].url, servers[0].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED })
271
272       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
273     })
274
275     it('Should not send a new video notification when a remote video becomes unlisted', async function () {
276       this.timeout(20000)
277
278       const videoNameId = 90
279       const videoName = 'remote video ' + videoNameId
280
281       const data = { privacy: VideoPrivacy.PRIVATE }
282       const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
283       await waitJobs(servers)
284
285       await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED })
286
287       await waitJobs(servers)
288       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
289     })
290
291     it('Should send a new video notification after a video import', async function () {
292       this.timeout(30000)
293
294       const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken)
295       const channelId = resChannel.body.videoChannels[0].id
296       const videoName = 'local video 100'
297
298       const attributes = {
299         name: videoName,
300         channelId,
301         privacy: VideoPrivacy.PUBLIC,
302         targetUrl: getYoutubeVideoUrl()
303       }
304       const res = await importVideo(servers[0].url, servers[0].accessToken, attributes)
305       const uuid = res.body.video.uuid
306
307       await waitJobs(servers)
308
309       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
310     })
311   })
312
313   describe('Comment on my video notifications', function () {
314     let baseParams: CheckerBaseParams
315
316     before(() => {
317       baseParams = {
318         server: servers[0],
319         emails,
320         socketNotifications: userNotifications,
321         token: userAccessToken
322       }
323     })
324
325     it('Should not send a new comment notification after a comment on another video', async function () {
326       this.timeout(10000)
327
328       const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'super video' })
329       const uuid = resVideo.body.video.uuid
330
331       const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment')
332       const commentId = resComment.body.comment.id
333
334       await wait(500)
335       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence')
336     })
337
338     it('Should not send a new comment notification if I comment my own video', async function () {
339       this.timeout(10000)
340
341       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' })
342       const uuid = resVideo.body.video.uuid
343
344       const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, uuid, 'comment')
345       const commentId = resComment.body.comment.id
346
347       await wait(500)
348       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'absence')
349     })
350
351     it('Should send a new comment notification after a local comment on my video', async function () {
352       this.timeout(10000)
353
354       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' })
355       const uuid = resVideo.body.video.uuid
356
357       const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment')
358       const commentId = resComment.body.comment.id
359
360       await wait(500)
361       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'presence')
362     })
363
364     it('Should send a new comment notification after a remote comment on my video', async function () {
365       this.timeout(10000)
366
367       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' })
368       const uuid = resVideo.body.video.uuid
369
370       await waitJobs(servers)
371
372       const resComment = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'comment')
373       const commentId = resComment.body.comment.id
374
375       await waitJobs(servers)
376       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, commentId, 'presence')
377     })
378
379     it('Should send a new comment notification after a local reply on my video', async function () {
380       this.timeout(10000)
381
382       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' })
383       const uuid = resVideo.body.video.uuid
384
385       const resThread = await addVideoCommentThread(servers[0].url, servers[0].accessToken, uuid, 'comment')
386       const threadId = resThread.body.comment.id
387
388       const resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, uuid, threadId, 'reply')
389       const commentId = resComment.body.comment.id
390
391       await wait(500)
392       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, threadId, 'presence')
393     })
394
395     it('Should send a new comment notification after a remote reply on my video', async function () {
396       this.timeout(10000)
397
398       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: 'super video' })
399       const uuid = resVideo.body.video.uuid
400       await waitJobs(servers)
401
402       const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'comment')
403       const threadId = resThread.body.comment.id
404
405       const resComment = await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, threadId, 'reply')
406       const commentId = resComment.body.comment.id
407
408       await waitJobs(servers)
409       await checkNewCommentOnMyVideo(baseParams, uuid, commentId, threadId, 'presence')
410     })
411   })
412
413   describe('Video abuse for moderators notification' , function () {
414     let baseParams: CheckerBaseParams
415
416     before(() => {
417       baseParams = {
418         server: servers[0],
419         emails,
420         socketNotifications: adminNotifications,
421         token: servers[0].accessToken
422       }
423     })
424
425     it('Should send a notification to moderators on local video abuse', async function () {
426       this.timeout(10000)
427
428       const videoName = 'local video 110'
429
430       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
431       const uuid = resVideo.body.video.uuid
432
433       await reportVideoAbuse(servers[0].url, servers[0].accessToken, uuid, 'super reason')
434
435       await waitJobs(servers)
436       await checkNewVideoAbuseForModerators(baseParams, uuid, videoName, 'presence')
437     })
438
439     it('Should send a notification to moderators on remote video abuse', async function () {
440       this.timeout(10000)
441
442       const videoName = 'remote video 120'
443
444       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
445       const uuid = resVideo.body.video.uuid
446
447       await waitJobs(servers)
448
449       await reportVideoAbuse(servers[1].url, servers[1].accessToken, uuid, 'super reason')
450
451       await waitJobs(servers)
452       await checkNewVideoAbuseForModerators(baseParams, uuid, videoName, 'presence')
453     })
454   })
455
456   describe('Video blacklist on my video', function () {
457     let baseParams: CheckerBaseParams
458
459     before(() => {
460       baseParams = {
461         server: servers[0],
462         emails,
463         socketNotifications: userNotifications,
464         token: userAccessToken
465       }
466     })
467
468     it('Should send a notification to video owner on blacklist', async function () {
469       this.timeout(10000)
470
471       const videoName = 'local video 130'
472
473       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
474       const uuid = resVideo.body.video.uuid
475
476       await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid)
477
478       await waitJobs(servers)
479       await checkNewBlacklistOnMyVideo(baseParams, uuid, videoName, 'blacklist')
480     })
481
482     it('Should send a notification to video owner on unblacklist', async function () {
483       this.timeout(10000)
484
485       const videoName = 'local video 130'
486
487       const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name: videoName })
488       const uuid = resVideo.body.video.uuid
489
490       await addVideoToBlacklist(servers[0].url, servers[0].accessToken, uuid)
491
492       await waitJobs(servers)
493       await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, uuid)
494       await waitJobs(servers)
495
496       await wait(500)
497       await checkNewBlacklistOnMyVideo(baseParams, uuid, videoName, 'unblacklist')
498     })
499   })
500
501   describe('Mark as read', function () {
502     it('Should mark as read some notifications', async function () {
503       const res = await getUserNotifications(servers[0].url, userAccessToken, 2, 3)
504       const ids = res.body.data.map(n => n.id)
505
506       await markAsReadNotifications(servers[0].url, userAccessToken, ids)
507     })
508
509     it('Should have the notifications marked as read', async function () {
510       const res = await getUserNotifications(servers[0].url, userAccessToken, 0, 10)
511
512       const notifications = res.body.data as UserNotification[]
513       expect(notifications[0].read).to.be.false
514       expect(notifications[1].read).to.be.false
515       expect(notifications[2].read).to.be.true
516       expect(notifications[3].read).to.be.true
517       expect(notifications[4].read).to.be.true
518       expect(notifications[5].read).to.be.false
519     })
520   })
521
522   describe('Notification settings', function () {
523     const baseUpdateNotificationParams = {
524       newCommentOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
525       newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
526       videoAbuseAsModerator: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL,
527       blacklistOnMyVideo: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
528     }
529     let baseParams: CheckerBaseParams
530
531     before(() => {
532       baseParams = {
533         server: servers[0],
534         emails,
535         socketNotifications: userNotifications,
536         token: userAccessToken
537       }
538     })
539
540     it('Should not have notifications', async function () {
541       await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
542         newVideoFromSubscription: UserNotificationSettingValue.NONE
543       }))
544
545       {
546         const res = await getMyUserInformation(servers[0].url, userAccessToken)
547         const info = res.body as User
548         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE)
549       }
550
551       const videoNameId = 42
552       const videoName = 'local video ' + videoNameId
553       const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
554
555       const check = { web: true, mail: true }
556       await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence')
557     })
558
559     it('Should only have web notifications', async function () {
560       await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
561         newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION
562       }))
563
564       {
565         const res = await getMyUserInformation(servers[0].url, userAccessToken)
566         const info = res.body as User
567         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION)
568       }
569
570       const videoNameId = 52
571       const videoName = 'local video ' + videoNameId
572       const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
573
574       {
575         const check = { mail: true, web: false }
576         await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence')
577       }
578
579       {
580         const check = { mail: false, web: true }
581         await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'presence')
582       }
583     })
584
585     it('Should only have mail notifications', async function () {
586       await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
587         newVideoFromSubscription: UserNotificationSettingValue.EMAIL
588       }))
589
590       {
591         const res = await getMyUserInformation(servers[0].url, userAccessToken)
592         const info = res.body as User
593         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL)
594       }
595
596       const videoNameId = 62
597       const videoName = 'local video ' + videoNameId
598       const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
599
600       {
601         const check = { mail: false, web: true }
602         await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'absence')
603       }
604
605       {
606         const check = { mail: true, web: false }
607         await checkNewVideoFromSubscription(immutableAssign(baseParams, { check }), videoName, uuid, 'presence')
608       }
609     })
610
611     it('Should have email and web notifications', async function () {
612       await updateMyNotificationSettings(servers[0].url, userAccessToken, immutableAssign(baseUpdateNotificationParams, {
613         newVideoFromSubscription: UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL
614       }))
615
616       {
617         const res = await getMyUserInformation(servers[0].url, userAccessToken)
618         const info = res.body as User
619         expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB_NOTIFICATION_AND_EMAIL)
620       }
621
622       const videoNameId = 72
623       const videoName = 'local video ' + videoNameId
624       const uuid = await uploadVideoByLocalAccount(servers, videoNameId)
625
626       await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
627     })
628   })
629
630   after(async function () {
631     killallServers(servers)
632   })
633 })