aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/tests/src/api/notifications
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-08-17 08:59:21 +0200
committerChocobozzz <me@florianbigard.com>2023-08-17 08:59:21 +0200
commitc380e3928517eb5311b38cf257816642617d7a33 (patch)
tree2ea9b70ebca16b5d109bcce98fe7f944dad89319 /packages/tests/src/api/notifications
parenta8ca6190fb462bf6eb5685cfc1d8ae444164a487 (diff)
parent3a4992633ee62d5edfbb484d9c6bcb3cf158489d (diff)
downloadPeerTube-c380e3928517eb5311b38cf257816642617d7a33.tar.gz
PeerTube-c380e3928517eb5311b38cf257816642617d7a33.tar.zst
PeerTube-c380e3928517eb5311b38cf257816642617d7a33.zip
Merge branch 'feature/esm-and-nx' into develop
Diffstat (limited to 'packages/tests/src/api/notifications')
-rw-r--r--packages/tests/src/api/notifications/admin-notifications.ts154
-rw-r--r--packages/tests/src/api/notifications/comments-notifications.ts300
-rw-r--r--packages/tests/src/api/notifications/index.ts6
-rw-r--r--packages/tests/src/api/notifications/moderation-notifications.ts609
-rw-r--r--packages/tests/src/api/notifications/notifications-api.ts206
-rw-r--r--packages/tests/src/api/notifications/registrations-notifications.ts83
-rw-r--r--packages/tests/src/api/notifications/user-notifications.ts574
7 files changed, 1932 insertions, 0 deletions
diff --git a/packages/tests/src/api/notifications/admin-notifications.ts b/packages/tests/src/api/notifications/admin-notifications.ts
new file mode 100644
index 000000000..2186dc55a
--- /dev/null
+++ b/packages/tests/src/api/notifications/admin-notifications.ts
@@ -0,0 +1,154 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { wait } from '@peertube/peertube-core-utils'
5import { PluginType, UserNotification, UserNotificationType } from '@peertube/peertube-models'
6import { cleanupTests, PeerTubeServer } from '@peertube/peertube-server-commands'
7import { MockSmtpServer } from '@tests/shared/mock-servers/mock-email.js'
8import { MockJoinPeerTubeVersions } from '@tests/shared/mock-servers/mock-joinpeertube-versions.js'
9import { CheckerBaseParams, prepareNotificationsTest, checkNewPeerTubeVersion, checkNewPluginVersion } from '@tests/shared/notifications.js'
10import { SQLCommand } from '@tests/shared/sql-command.js'
11
12describe('Test admin notifications', function () {
13 let server: PeerTubeServer
14 let sqlCommand: SQLCommand
15 let userNotifications: UserNotification[] = []
16 let adminNotifications: UserNotification[] = []
17 let emails: object[] = []
18 let baseParams: CheckerBaseParams
19 let joinPeerTubeServer: MockJoinPeerTubeVersions
20
21 before(async function () {
22 this.timeout(120000)
23
24 joinPeerTubeServer = new MockJoinPeerTubeVersions()
25 const port = await joinPeerTubeServer.initialize()
26
27 const config = {
28 peertube: {
29 check_latest_version: {
30 enabled: true,
31 url: `http://127.0.0.1:${port}/versions.json`
32 }
33 },
34 plugins: {
35 index: {
36 enabled: true,
37 check_latest_versions_interval: '3 seconds'
38 }
39 }
40 }
41
42 const res = await prepareNotificationsTest(1, config)
43 emails = res.emails
44 server = res.servers[0]
45
46 userNotifications = res.userNotifications
47 adminNotifications = res.adminNotifications
48
49 baseParams = {
50 server,
51 emails,
52 socketNotifications: adminNotifications,
53 token: server.accessToken
54 }
55
56 await server.plugins.install({ npmName: 'peertube-plugin-hello-world' })
57 await server.plugins.install({ npmName: 'peertube-theme-background-red' })
58
59 sqlCommand = new SQLCommand(server)
60 })
61
62 describe('Latest PeerTube version notification', function () {
63
64 it('Should not send a notification to admins if there is no new version', async function () {
65 this.timeout(30000)
66
67 joinPeerTubeServer.setLatestVersion('1.4.2')
68
69 await wait(3000)
70 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '1.4.2', checkType: 'absence' })
71 })
72
73 it('Should send a notification to admins on new version', async function () {
74 this.timeout(30000)
75
76 joinPeerTubeServer.setLatestVersion('15.4.2')
77
78 await wait(3000)
79 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '15.4.2', checkType: 'presence' })
80 })
81
82 it('Should not send the same notification to admins', async function () {
83 this.timeout(30000)
84
85 await wait(3000)
86 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(1)
87 })
88
89 it('Should not have sent a notification to users', async function () {
90 this.timeout(30000)
91
92 expect(userNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(0)
93 })
94
95 it('Should send a new notification after a new release', async function () {
96 this.timeout(30000)
97
98 joinPeerTubeServer.setLatestVersion('15.4.3')
99
100 await wait(3000)
101 await checkNewPeerTubeVersion({ ...baseParams, latestVersion: '15.4.3', checkType: 'presence' })
102 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
103 })
104 })
105
106 describe('Latest plugin version notification', function () {
107
108 it('Should not send a notification to admins if there is no new plugin version', async function () {
109 this.timeout(30000)
110
111 await wait(6000)
112 await checkNewPluginVersion({ ...baseParams, pluginType: PluginType.PLUGIN, pluginName: 'hello-world', checkType: 'absence' })
113 })
114
115 it('Should send a notification to admins on new plugin version', async function () {
116 this.timeout(30000)
117
118 await sqlCommand.setPluginVersion('hello-world', '0.0.1')
119 await sqlCommand.setPluginLatestVersion('hello-world', '0.0.1')
120 await wait(6000)
121
122 await checkNewPluginVersion({ ...baseParams, pluginType: PluginType.PLUGIN, pluginName: 'hello-world', checkType: 'presence' })
123 })
124
125 it('Should not send the same notification to admins', async function () {
126 this.timeout(30000)
127
128 await wait(6000)
129
130 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PLUGIN_VERSION)).to.have.lengthOf(1)
131 })
132
133 it('Should not have sent a notification to users', async function () {
134 expect(userNotifications.filter(n => n.type === UserNotificationType.NEW_PLUGIN_VERSION)).to.have.lengthOf(0)
135 })
136
137 it('Should send a new notification after a new plugin release', async function () {
138 this.timeout(30000)
139
140 await sqlCommand.setPluginVersion('hello-world', '0.0.1')
141 await sqlCommand.setPluginLatestVersion('hello-world', '0.0.1')
142 await wait(6000)
143
144 expect(adminNotifications.filter(n => n.type === UserNotificationType.NEW_PEERTUBE_VERSION)).to.have.lengthOf(2)
145 })
146 })
147
148 after(async function () {
149 MockSmtpServer.Instance.kill()
150
151 await sqlCommand.cleanup()
152 await cleanupTests([ server ])
153 })
154})
diff --git a/packages/tests/src/api/notifications/comments-notifications.ts b/packages/tests/src/api/notifications/comments-notifications.ts
new file mode 100644
index 000000000..5647d1286
--- /dev/null
+++ b/packages/tests/src/api/notifications/comments-notifications.ts
@@ -0,0 +1,300 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { UserNotification } from '@peertube/peertube-models'
5import { cleanupTests, PeerTubeServer, waitJobs } from '@peertube/peertube-server-commands'
6import { MockSmtpServer } from '@tests/shared/mock-servers/mock-email.js'
7import { prepareNotificationsTest, CheckerBaseParams, checkNewCommentOnMyVideo, checkCommentMention } from '@tests/shared/notifications.js'
8
9describe('Test comments notifications', function () {
10 let servers: PeerTubeServer[] = []
11 let userToken: string
12 let userNotifications: UserNotification[] = []
13 let emails: object[] = []
14
15 const commentText = '**hello** <a href="https://joinpeertube.org">world</a>, <h1>what do you think about peertube?</h1>'
16 const expectedHtml = '<strong>hello</strong> <a href="https://joinpeertube.org" target="_blank" rel="noopener noreferrer">world</a>' +
17 ', </p>what do you think about peertube?'
18
19 before(async function () {
20 this.timeout(120000)
21
22 const res = await prepareNotificationsTest(2)
23 emails = res.emails
24 userToken = res.userAccessToken
25 servers = res.servers
26 userNotifications = res.userNotifications
27 })
28
29 describe('Comment on my video notifications', function () {
30 let baseParams: CheckerBaseParams
31
32 before(() => {
33 baseParams = {
34 server: servers[0],
35 emails,
36 socketNotifications: userNotifications,
37 token: userToken
38 }
39 })
40
41 it('Should not send a new comment notification after a comment on another video', async function () {
42 this.timeout(30000)
43
44 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
45
46 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
47 const commentId = created.id
48
49 await waitJobs(servers)
50 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
51 })
52
53 it('Should not send a new comment notification if I comment my own video', async function () {
54 this.timeout(30000)
55
56 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
57
58 const created = await servers[0].comments.createThread({ token: userToken, videoId: uuid, text: 'comment' })
59 const commentId = created.id
60
61 await waitJobs(servers)
62 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
63 })
64
65 it('Should not send a new comment notification if the account is muted', async function () {
66 this.timeout(30000)
67
68 await servers[0].blocklist.addToMyBlocklist({ token: userToken, account: 'root' })
69
70 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
71
72 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
73 const commentId = created.id
74
75 await waitJobs(servers)
76 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'absence' })
77
78 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken, account: 'root' })
79 })
80
81 it('Should send a new comment notification after a local comment on my video', async function () {
82 this.timeout(30000)
83
84 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
85
86 const created = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
87 const commentId = created.id
88
89 await waitJobs(servers)
90 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'presence' })
91 })
92
93 it('Should send a new comment notification after a remote comment on my video', async function () {
94 this.timeout(30000)
95
96 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
97
98 await waitJobs(servers)
99
100 await servers[1].comments.createThread({ videoId: uuid, text: 'comment' })
101
102 await waitJobs(servers)
103
104 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
105 expect(data).to.have.lengthOf(1)
106
107 const commentId = data[0].id
108 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId: commentId, commentId, checkType: 'presence' })
109 })
110
111 it('Should send a new comment notification after a local reply on my video', async function () {
112 this.timeout(30000)
113
114 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
115
116 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: 'comment' })
117
118 const { id: commentId } = await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'reply' })
119
120 await waitJobs(servers)
121 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId, commentId, checkType: 'presence' })
122 })
123
124 it('Should send a new comment notification after a remote reply on my video', async function () {
125 this.timeout(30000)
126
127 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
128 await waitJobs(servers)
129
130 {
131 const created = await servers[1].comments.createThread({ videoId: uuid, text: 'comment' })
132 const threadId = created.id
133 await servers[1].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'reply' })
134 }
135
136 await waitJobs(servers)
137
138 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
139 expect(data).to.have.lengthOf(1)
140
141 const threadId = data[0].id
142 const tree = await servers[0].comments.getThread({ videoId: uuid, threadId })
143
144 expect(tree.children).to.have.lengthOf(1)
145 const commentId = tree.children[0].comment.id
146
147 await checkNewCommentOnMyVideo({ ...baseParams, shortUUID, threadId, commentId, checkType: 'presence' })
148 })
149
150 it('Should convert markdown in comment to html', async function () {
151 this.timeout(30000)
152
153 const { uuid } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'cool video' } })
154
155 await servers[0].comments.createThread({ videoId: uuid, text: commentText })
156
157 await waitJobs(servers)
158
159 const latestEmail = emails[emails.length - 1]
160 expect(latestEmail['html']).to.contain(expectedHtml)
161 })
162 })
163
164 describe('Mention notifications', function () {
165 let baseParams: CheckerBaseParams
166 const byAccountDisplayName = 'super root name'
167
168 before(async function () {
169 baseParams = {
170 server: servers[0],
171 emails,
172 socketNotifications: userNotifications,
173 token: userToken
174 }
175
176 await servers[0].users.updateMe({ displayName: 'super root name' })
177 await servers[1].users.updateMe({ displayName: 'super root 2 name' })
178 })
179
180 it('Should not send a new mention comment notification if I mention the video owner', async function () {
181 this.timeout(30000)
182
183 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken, attributes: { name: 'super video' } })
184
185 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
186
187 await waitJobs(servers)
188 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
189 })
190
191 it('Should not send a new mention comment notification if I mention myself', async function () {
192 this.timeout(30000)
193
194 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
195
196 const { id: commentId } = await servers[0].comments.createThread({ token: userToken, videoId: uuid, text: '@user_1 hello' })
197
198 await waitJobs(servers)
199 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
200 })
201
202 it('Should not send a new mention notification if the account is muted', async function () {
203 this.timeout(30000)
204
205 await servers[0].blocklist.addToMyBlocklist({ token: userToken, account: 'root' })
206
207 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
208
209 const { id: commentId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
210
211 await waitJobs(servers)
212 await checkCommentMention({ ...baseParams, shortUUID, threadId: commentId, commentId, byAccountDisplayName, checkType: 'absence' })
213
214 await servers[0].blocklist.removeFromMyBlocklist({ token: userToken, account: 'root' })
215 })
216
217 it('Should not send a new mention notification if the remote account mention a local account', async function () {
218 this.timeout(30000)
219
220 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
221
222 await waitJobs(servers)
223 const { id: threadId } = await servers[1].comments.createThread({ videoId: uuid, text: '@user_1 hello' })
224
225 await waitJobs(servers)
226
227 const byAccountDisplayName = 'super root 2 name'
228 await checkCommentMention({ ...baseParams, shortUUID, threadId, commentId: threadId, byAccountDisplayName, checkType: 'absence' })
229 })
230
231 it('Should send a new mention notification after local comments', async function () {
232 this.timeout(30000)
233
234 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
235
236 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hellotext: 1' })
237
238 await waitJobs(servers)
239 await checkCommentMention({ ...baseParams, shortUUID, threadId, commentId: threadId, byAccountDisplayName, checkType: 'presence' })
240
241 const { id: commentId } = await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: 'hello 2 @user_1' })
242
243 await waitJobs(servers)
244 await checkCommentMention({ ...baseParams, shortUUID, commentId, threadId, byAccountDisplayName, checkType: 'presence' })
245 })
246
247 it('Should send a new mention notification after remote comments', async function () {
248 this.timeout(30000)
249
250 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
251
252 await waitJobs(servers)
253
254 const text1 = `hello @user_1@${servers[0].host} 1`
255 const { id: server2ThreadId } = await servers[1].comments.createThread({ videoId: uuid, text: text1 })
256
257 await waitJobs(servers)
258
259 const { data } = await servers[0].comments.listThreads({ videoId: uuid })
260 expect(data).to.have.lengthOf(1)
261
262 const byAccountDisplayName = 'super root 2 name'
263 const threadId = data[0].id
264 await checkCommentMention({ ...baseParams, shortUUID, commentId: threadId, threadId, byAccountDisplayName, checkType: 'presence' })
265
266 const text2 = `@user_1@${servers[0].host} hello 2 @root@${servers[0].host}`
267 await servers[1].comments.addReply({ videoId: uuid, toCommentId: server2ThreadId, text: text2 })
268
269 await waitJobs(servers)
270
271 const tree = await servers[0].comments.getThread({ videoId: uuid, threadId })
272
273 expect(tree.children).to.have.lengthOf(1)
274 const commentId = tree.children[0].comment.id
275
276 await checkCommentMention({ ...baseParams, shortUUID, commentId, threadId, byAccountDisplayName, checkType: 'presence' })
277 })
278
279 it('Should convert markdown in comment to html', async function () {
280 this.timeout(30000)
281
282 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'super video' } })
283
284 const { id: threadId } = await servers[0].comments.createThread({ videoId: uuid, text: '@user_1 hello 1' })
285
286 await servers[0].comments.addReply({ videoId: uuid, toCommentId: threadId, text: '@user_1 ' + commentText })
287
288 await waitJobs(servers)
289
290 const latestEmail = emails[emails.length - 1]
291 expect(latestEmail['html']).to.contain(expectedHtml)
292 })
293 })
294
295 after(async function () {
296 MockSmtpServer.Instance.kill()
297
298 await cleanupTests(servers)
299 })
300})
diff --git a/packages/tests/src/api/notifications/index.ts b/packages/tests/src/api/notifications/index.ts
new file mode 100644
index 000000000..d63d94182
--- /dev/null
+++ b/packages/tests/src/api/notifications/index.ts
@@ -0,0 +1,6 @@
1import './admin-notifications.js'
2import './comments-notifications.js'
3import './moderation-notifications.js'
4import './notifications-api.js'
5import './registrations-notifications.js'
6import './user-notifications.js'
diff --git a/packages/tests/src/api/notifications/moderation-notifications.ts b/packages/tests/src/api/notifications/moderation-notifications.ts
new file mode 100644
index 000000000..493764882
--- /dev/null
+++ b/packages/tests/src/api/notifications/moderation-notifications.ts
@@ -0,0 +1,609 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { wait } from '@peertube/peertube-core-utils'
4import { AbuseState, CustomConfig, UserNotification, UserRole, VideoPrivacy } from '@peertube/peertube-models'
5import { buildUUID } from '@peertube/peertube-node-utils'
6import { cleanupTests, PeerTubeServer, waitJobs } from '@peertube/peertube-server-commands'
7import { MockSmtpServer } from '@tests/shared/mock-servers/mock-email.js'
8import { MockInstancesIndex } from '@tests/shared/mock-servers/mock-instances-index.js'
9import {
10 prepareNotificationsTest,
11 CheckerBaseParams,
12 checkNewVideoAbuseForModerators,
13 checkNewCommentAbuseForModerators,
14 checkNewAccountAbuseForModerators,
15 checkAbuseStateChange,
16 checkNewAbuseMessage,
17 checkNewBlacklistOnMyVideo,
18 checkNewInstanceFollower,
19 checkAutoInstanceFollowing,
20 checkVideoAutoBlacklistForModerators,
21 checkVideoIsPublished,
22 checkNewVideoFromSubscription
23} from '@tests/shared/notifications.js'
24
25describe('Test moderation notifications', function () {
26 let servers: PeerTubeServer[] = []
27 let userToken1: string
28 let userToken2: string
29
30 let userNotifications: UserNotification[] = []
31 let adminNotifications: UserNotification[] = []
32 let adminNotificationsServer2: UserNotification[] = []
33 let emails: object[] = []
34
35 before(async function () {
36 this.timeout(120000)
37
38 const res = await prepareNotificationsTest(3)
39 emails = res.emails
40 userToken1 = res.userAccessToken
41 servers = res.servers
42 userNotifications = res.userNotifications
43 adminNotifications = res.adminNotifications
44 adminNotificationsServer2 = res.adminNotificationsServer2
45
46 userToken2 = await servers[1].users.generateUserAndToken('user2', UserRole.USER)
47 })
48
49 describe('Abuse for moderators notification', function () {
50 let baseParams: CheckerBaseParams
51
52 before(() => {
53 baseParams = {
54 server: servers[0],
55 emails,
56 socketNotifications: adminNotifications,
57 token: servers[0].accessToken
58 }
59 })
60
61 it('Should not send a notification to moderators on local abuse reported by an admin', async function () {
62 this.timeout(50000)
63
64 const name = 'video for abuse ' + buildUUID()
65 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
66
67 await servers[0].abuses.report({ videoId: video.id, reason: 'super reason' })
68
69 await waitJobs(servers)
70 await checkNewVideoAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'absence' })
71 })
72
73 it('Should send a notification to moderators on local video abuse', async function () {
74 this.timeout(50000)
75
76 const name = 'video for abuse ' + buildUUID()
77 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
78
79 await servers[0].abuses.report({ token: userToken1, videoId: video.id, reason: 'super reason' })
80
81 await waitJobs(servers)
82 await checkNewVideoAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
83 })
84
85 it('Should send a notification to moderators on remote video abuse', async function () {
86 this.timeout(50000)
87
88 const name = 'video for abuse ' + buildUUID()
89 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
90
91 await waitJobs(servers)
92
93 const videoId = await servers[1].videos.getId({ uuid: video.uuid })
94 await servers[1].abuses.report({ token: userToken2, videoId, reason: 'super reason' })
95
96 await waitJobs(servers)
97 await checkNewVideoAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
98 })
99
100 it('Should send a notification to moderators on local comment abuse', async function () {
101 this.timeout(50000)
102
103 const name = 'video for abuse ' + buildUUID()
104 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
105 const comment = await servers[0].comments.createThread({
106 token: userToken1,
107 videoId: video.id,
108 text: 'comment abuse ' + buildUUID()
109 })
110
111 await waitJobs(servers)
112
113 await servers[0].abuses.report({ token: userToken1, commentId: comment.id, reason: 'super reason' })
114
115 await waitJobs(servers)
116 await checkNewCommentAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
117 })
118
119 it('Should send a notification to moderators on remote comment abuse', async function () {
120 this.timeout(50000)
121
122 const name = 'video for abuse ' + buildUUID()
123 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
124
125 await servers[0].comments.createThread({
126 token: userToken1,
127 videoId: video.id,
128 text: 'comment abuse ' + buildUUID()
129 })
130
131 await waitJobs(servers)
132
133 const { data } = await servers[1].comments.listThreads({ videoId: video.uuid })
134 const commentId = data[0].id
135 await servers[1].abuses.report({ token: userToken2, commentId, reason: 'super reason' })
136
137 await waitJobs(servers)
138 await checkNewCommentAbuseForModerators({ ...baseParams, shortUUID: video.shortUUID, videoName: name, checkType: 'presence' })
139 })
140
141 it('Should send a notification to moderators on local account abuse', async function () {
142 this.timeout(50000)
143
144 const username = 'user' + new Date().getTime()
145 const { account } = await servers[0].users.create({ username, password: 'donald' })
146 const accountId = account.id
147
148 await servers[0].abuses.report({ token: userToken1, accountId, reason: 'super reason' })
149
150 await waitJobs(servers)
151 await checkNewAccountAbuseForModerators({ ...baseParams, displayName: username, checkType: 'presence' })
152 })
153
154 it('Should send a notification to moderators on remote account abuse', async function () {
155 this.timeout(50000)
156
157 const username = 'user' + new Date().getTime()
158 const tmpToken = await servers[0].users.generateUserAndToken(username)
159 await servers[0].videos.upload({ token: tmpToken, attributes: { name: 'super video' } })
160
161 await waitJobs(servers)
162
163 const account = await servers[1].accounts.get({ accountName: username + '@' + servers[0].host })
164 await servers[1].abuses.report({ token: userToken2, accountId: account.id, reason: 'super reason' })
165
166 await waitJobs(servers)
167 await checkNewAccountAbuseForModerators({ ...baseParams, displayName: username, checkType: 'presence' })
168 })
169 })
170
171 describe('Abuse state change notification', function () {
172 let baseParams: CheckerBaseParams
173 let abuseId: number
174
175 before(async function () {
176 baseParams = {
177 server: servers[0],
178 emails,
179 socketNotifications: userNotifications,
180 token: userToken1
181 }
182
183 const name = 'abuse ' + buildUUID()
184 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
185
186 const body = await servers[0].abuses.report({ token: userToken1, videoId: video.id, reason: 'super reason' })
187 abuseId = body.abuse.id
188 })
189
190 it('Should send a notification to reporter if the abuse has been accepted', async function () {
191 this.timeout(30000)
192
193 await servers[0].abuses.update({ abuseId, body: { state: AbuseState.ACCEPTED } })
194 await waitJobs(servers)
195
196 await checkAbuseStateChange({ ...baseParams, abuseId, state: AbuseState.ACCEPTED, checkType: 'presence' })
197 })
198
199 it('Should send a notification to reporter if the abuse has been rejected', async function () {
200 this.timeout(30000)
201
202 await servers[0].abuses.update({ abuseId, body: { state: AbuseState.REJECTED } })
203 await waitJobs(servers)
204
205 await checkAbuseStateChange({ ...baseParams, abuseId, state: AbuseState.REJECTED, checkType: 'presence' })
206 })
207 })
208
209 describe('New abuse message notification', function () {
210 let baseParamsUser: CheckerBaseParams
211 let baseParamsAdmin: CheckerBaseParams
212 let abuseId: number
213 let abuseId2: number
214
215 before(async function () {
216 baseParamsUser = {
217 server: servers[0],
218 emails,
219 socketNotifications: userNotifications,
220 token: userToken1
221 }
222
223 baseParamsAdmin = {
224 server: servers[0],
225 emails,
226 socketNotifications: adminNotifications,
227 token: servers[0].accessToken
228 }
229
230 const name = 'abuse ' + buildUUID()
231 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
232
233 {
234 const body = await servers[0].abuses.report({ token: userToken1, videoId: video.id, reason: 'super reason' })
235 abuseId = body.abuse.id
236 }
237
238 {
239 const body = await servers[0].abuses.report({ token: userToken1, videoId: video.id, reason: 'super reason 2' })
240 abuseId2 = body.abuse.id
241 }
242 })
243
244 it('Should send a notification to reporter on new message', async function () {
245 this.timeout(30000)
246
247 const message = 'my super message to users'
248 await servers[0].abuses.addMessage({ abuseId, message })
249 await waitJobs(servers)
250
251 await checkNewAbuseMessage({ ...baseParamsUser, abuseId, message, toEmail: 'user_1@example.com', checkType: 'presence' })
252 })
253
254 it('Should not send a notification to the admin if sent by the admin', async function () {
255 this.timeout(30000)
256
257 const message = 'my super message that should not be sent to the admin'
258 await servers[0].abuses.addMessage({ abuseId, message })
259 await waitJobs(servers)
260
261 const toEmail = 'admin' + servers[0].internalServerNumber + '@example.com'
262 await checkNewAbuseMessage({ ...baseParamsAdmin, abuseId, message, toEmail, checkType: 'absence' })
263 })
264
265 it('Should send a notification to moderators', async function () {
266 this.timeout(30000)
267
268 const message = 'my super message to moderators'
269 await servers[0].abuses.addMessage({ token: userToken1, abuseId: abuseId2, message })
270 await waitJobs(servers)
271
272 const toEmail = 'admin' + servers[0].internalServerNumber + '@example.com'
273 await checkNewAbuseMessage({ ...baseParamsAdmin, abuseId: abuseId2, message, toEmail, checkType: 'presence' })
274 })
275
276 it('Should not send a notification to reporter if sent by the reporter', async function () {
277 this.timeout(30000)
278
279 const message = 'my super message that should not be sent to reporter'
280 await servers[0].abuses.addMessage({ token: userToken1, abuseId: abuseId2, message })
281 await waitJobs(servers)
282
283 const toEmail = 'user_1@example.com'
284 await checkNewAbuseMessage({ ...baseParamsUser, abuseId: abuseId2, message, toEmail, checkType: 'absence' })
285 })
286 })
287
288 describe('Video blacklist on my video', function () {
289 let baseParams: CheckerBaseParams
290
291 before(() => {
292 baseParams = {
293 server: servers[0],
294 emails,
295 socketNotifications: userNotifications,
296 token: userToken1
297 }
298 })
299
300 it('Should send a notification to video owner on blacklist', async function () {
301 this.timeout(30000)
302
303 const name = 'video for abuse ' + buildUUID()
304 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
305
306 await servers[0].blacklist.add({ videoId: uuid })
307
308 await waitJobs(servers)
309 await checkNewBlacklistOnMyVideo({ ...baseParams, shortUUID, videoName: name, blacklistType: 'blacklist' })
310 })
311
312 it('Should send a notification to video owner on unblacklist', async function () {
313 this.timeout(30000)
314
315 const name = 'video for abuse ' + buildUUID()
316 const { uuid, shortUUID } = await servers[0].videos.upload({ token: userToken1, attributes: { name } })
317
318 await servers[0].blacklist.add({ videoId: uuid })
319
320 await waitJobs(servers)
321 await servers[0].blacklist.remove({ videoId: uuid })
322 await waitJobs(servers)
323
324 await wait(500)
325 await checkNewBlacklistOnMyVideo({ ...baseParams, shortUUID, videoName: name, blacklistType: 'unblacklist' })
326 })
327 })
328
329 describe('New instance follows', function () {
330 const instanceIndexServer = new MockInstancesIndex()
331 let config: any
332 let baseParams: CheckerBaseParams
333
334 before(async function () {
335 baseParams = {
336 server: servers[0],
337 emails,
338 socketNotifications: adminNotifications,
339 token: servers[0].accessToken
340 }
341
342 const port = await instanceIndexServer.initialize()
343 instanceIndexServer.addInstance(servers[1].host)
344
345 config = {
346 followings: {
347 instance: {
348 autoFollowIndex: {
349 indexUrl: `http://127.0.0.1:${port}/api/v1/instances/hosts`,
350 enabled: true
351 }
352 }
353 }
354 }
355 })
356
357 it('Should send a notification only to admin when there is a new instance follower', async function () {
358 this.timeout(60000)
359
360 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
361
362 await waitJobs(servers)
363
364 await checkNewInstanceFollower({ ...baseParams, followerHost: servers[2].host, checkType: 'presence' })
365
366 const userOverride = { socketNotifications: userNotifications, token: userToken1, check: { web: true, mail: false } }
367 await checkNewInstanceFollower({ ...baseParams, ...userOverride, followerHost: servers[2].host, checkType: 'absence' })
368 })
369
370 it('Should send a notification on auto follow back', async function () {
371 this.timeout(40000)
372
373 await servers[2].follows.unfollow({ target: servers[0] })
374 await waitJobs(servers)
375
376 const config = {
377 followings: {
378 instance: {
379 autoFollowBack: { enabled: true }
380 }
381 }
382 }
383 await servers[0].config.updateCustomSubConfig({ newConfig: config })
384
385 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
386
387 await waitJobs(servers)
388
389 const followerHost = servers[0].host
390 const followingHost = servers[2].host
391 await checkAutoInstanceFollowing({ ...baseParams, followerHost, followingHost, checkType: 'presence' })
392
393 const userOverride = { socketNotifications: userNotifications, token: userToken1, check: { web: true, mail: false } }
394 await checkAutoInstanceFollowing({ ...baseParams, ...userOverride, followerHost, followingHost, checkType: 'absence' })
395
396 config.followings.instance.autoFollowBack.enabled = false
397 await servers[0].config.updateCustomSubConfig({ newConfig: config })
398 await servers[0].follows.unfollow({ target: servers[2] })
399 await servers[2].follows.unfollow({ target: servers[0] })
400 })
401
402 it('Should send a notification on auto instances index follow', async function () {
403 this.timeout(30000)
404 await servers[0].follows.unfollow({ target: servers[1] })
405
406 await servers[0].config.updateCustomSubConfig({ newConfig: config })
407
408 await wait(5000)
409 await waitJobs(servers)
410
411 const followerHost = servers[0].host
412 const followingHost = servers[1].host
413 await checkAutoInstanceFollowing({ ...baseParams, followerHost, followingHost, checkType: 'presence' })
414
415 config.followings.instance.autoFollowIndex.enabled = false
416 await servers[0].config.updateCustomSubConfig({ newConfig: config })
417 await servers[0].follows.unfollow({ target: servers[1] })
418 })
419 })
420
421 describe('Video-related notifications when video auto-blacklist is enabled', function () {
422 let userBaseParams: CheckerBaseParams
423 let adminBaseParamsServer1: CheckerBaseParams
424 let adminBaseParamsServer2: CheckerBaseParams
425 let uuid: string
426 let shortUUID: string
427 let videoName: string
428 let currentCustomConfig: CustomConfig
429
430 before(async function () {
431
432 adminBaseParamsServer1 = {
433 server: servers[0],
434 emails,
435 socketNotifications: adminNotifications,
436 token: servers[0].accessToken
437 }
438
439 adminBaseParamsServer2 = {
440 server: servers[1],
441 emails,
442 socketNotifications: adminNotificationsServer2,
443 token: servers[1].accessToken
444 }
445
446 userBaseParams = {
447 server: servers[0],
448 emails,
449 socketNotifications: userNotifications,
450 token: userToken1
451 }
452
453 currentCustomConfig = await servers[0].config.getCustomConfig()
454
455 const autoBlacklistTestsCustomConfig = {
456 ...currentCustomConfig,
457
458 autoBlacklist: {
459 videos: {
460 ofUsers: {
461 enabled: true
462 }
463 }
464 }
465 }
466
467 // enable transcoding otherwise own publish notification after transcoding not expected
468 autoBlacklistTestsCustomConfig.transcoding.enabled = true
469 await servers[0].config.updateCustomConfig({ newCustomConfig: autoBlacklistTestsCustomConfig })
470
471 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@' + servers[0].host })
472 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@' + servers[0].host })
473 })
474
475 it('Should send notification to moderators on new video with auto-blacklist', async function () {
476 this.timeout(120000)
477
478 videoName = 'video with auto-blacklist ' + buildUUID()
479 const video = await servers[0].videos.upload({ token: userToken1, attributes: { name: videoName } })
480 shortUUID = video.shortUUID
481 uuid = video.uuid
482
483 await waitJobs(servers)
484 await checkVideoAutoBlacklistForModerators({ ...adminBaseParamsServer1, shortUUID, videoName, checkType: 'presence' })
485 })
486
487 it('Should not send video publish notification if auto-blacklisted', async function () {
488 this.timeout(120000)
489
490 await checkVideoIsPublished({ ...userBaseParams, videoName, shortUUID, checkType: 'absence' })
491 })
492
493 it('Should not send a local user subscription notification if auto-blacklisted', async function () {
494 this.timeout(120000)
495
496 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'absence' })
497 })
498
499 it('Should not send a remote user subscription notification if auto-blacklisted', async function () {
500 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'absence' })
501 })
502
503 it('Should send video published and unblacklist after video unblacklisted', async function () {
504 this.timeout(120000)
505
506 await servers[0].blacklist.remove({ videoId: uuid })
507
508 await waitJobs(servers)
509
510 // FIXME: Can't test as two notifications sent to same user and util only checks last one
511 // One notification might be better anyways
512 // await checkNewBlacklistOnMyVideo(userBaseParams, videoUUID, videoName, 'unblacklist')
513 // await checkVideoIsPublished(userBaseParams, videoName, videoUUID, 'presence')
514 })
515
516 it('Should send a local user subscription notification after removed from blacklist', async function () {
517 this.timeout(120000)
518
519 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName, shortUUID, checkType: 'presence' })
520 })
521
522 it('Should send a remote user subscription notification after removed from blacklist', async function () {
523 this.timeout(120000)
524
525 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName, shortUUID, checkType: 'presence' })
526 })
527
528 it('Should send unblacklist but not published/subscription notes after unblacklisted if scheduled update pending', async function () {
529 this.timeout(120000)
530
531 const updateAt = new Date(new Date().getTime() + 1000000)
532
533 const name = 'video with auto-blacklist and future schedule ' + buildUUID()
534
535 const attributes = {
536 name,
537 privacy: VideoPrivacy.PRIVATE,
538 scheduleUpdate: {
539 updateAt: updateAt.toISOString(),
540 privacy: VideoPrivacy.PUBLIC
541 }
542 }
543
544 const { shortUUID, uuid } = await servers[0].videos.upload({ token: userToken1, attributes })
545
546 await servers[0].blacklist.remove({ videoId: uuid })
547
548 await waitJobs(servers)
549 await checkNewBlacklistOnMyVideo({ ...userBaseParams, shortUUID, videoName: name, blacklistType: 'unblacklist' })
550
551 // FIXME: Can't test absence as two notifications sent to same user and util only checks last one
552 // One notification might be better anyways
553 // await checkVideoIsPublished(userBaseParams, name, uuid, 'absence')
554
555 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
556 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
557 })
558
559 it('Should not send publish/subscription notifications after scheduled update if video still auto-blacklisted', async function () {
560 this.timeout(120000)
561
562 // In 2 seconds
563 const updateAt = new Date(new Date().getTime() + 2000)
564
565 const name = 'video with schedule done and still auto-blacklisted ' + buildUUID()
566
567 const attributes = {
568 name,
569 privacy: VideoPrivacy.PRIVATE,
570 scheduleUpdate: {
571 updateAt: updateAt.toISOString(),
572 privacy: VideoPrivacy.PUBLIC
573 }
574 }
575
576 const { shortUUID } = await servers[0].videos.upload({ token: userToken1, attributes })
577
578 await wait(6000)
579 await checkVideoIsPublished({ ...userBaseParams, videoName: name, shortUUID, checkType: 'absence' })
580 await checkNewVideoFromSubscription({ ...adminBaseParamsServer1, videoName: name, shortUUID, checkType: 'absence' })
581 await checkNewVideoFromSubscription({ ...adminBaseParamsServer2, videoName: name, shortUUID, checkType: 'absence' })
582 })
583
584 it('Should not send a notification to moderators on new video without auto-blacklist', async function () {
585 this.timeout(120000)
586
587 const name = 'video without auto-blacklist ' + buildUUID()
588
589 // admin with blacklist right will not be auto-blacklisted
590 const { shortUUID } = await servers[0].videos.upload({ attributes: { name } })
591
592 await waitJobs(servers)
593 await checkVideoAutoBlacklistForModerators({ ...adminBaseParamsServer1, shortUUID, videoName: name, checkType: 'absence' })
594 })
595
596 after(async () => {
597 await servers[0].config.updateCustomConfig({ newCustomConfig: currentCustomConfig })
598
599 await servers[0].subscriptions.remove({ uri: 'user_1_channel@' + servers[0].host })
600 await servers[1].subscriptions.remove({ uri: 'user_1_channel@' + servers[0].host })
601 })
602 })
603
604 after(async function () {
605 MockSmtpServer.Instance.kill()
606
607 await cleanupTests(servers)
608 })
609})
diff --git a/packages/tests/src/api/notifications/notifications-api.ts b/packages/tests/src/api/notifications/notifications-api.ts
new file mode 100644
index 000000000..1c7461553
--- /dev/null
+++ b/packages/tests/src/api/notifications/notifications-api.ts
@@ -0,0 +1,206 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { UserNotification, UserNotificationSettingValue } from '@peertube/peertube-models'
5import { cleanupTests, PeerTubeServer, waitJobs } from '@peertube/peertube-server-commands'
6import { MockSmtpServer } from '@tests/shared/mock-servers/mock-email.js'
7import {
8 prepareNotificationsTest,
9 CheckerBaseParams,
10 getAllNotificationsSettings,
11 checkNewVideoFromSubscription
12} from '@tests/shared/notifications.js'
13
14describe('Test notifications API', function () {
15 let server: PeerTubeServer
16 let userNotifications: UserNotification[] = []
17 let userToken: string
18 let emails: object[] = []
19
20 before(async function () {
21 this.timeout(120000)
22
23 const res = await prepareNotificationsTest(1)
24 emails = res.emails
25 userToken = res.userAccessToken
26 userNotifications = res.userNotifications
27 server = res.servers[0]
28
29 await server.subscriptions.add({ token: userToken, targetUri: 'root_channel@' + server.host })
30
31 for (let i = 0; i < 10; i++) {
32 await server.videos.randomUpload({ wait: false })
33 }
34
35 await waitJobs([ server ])
36 })
37
38 describe('Notification list & count', function () {
39
40 it('Should correctly list notifications', async function () {
41 const { data, total } = await server.notifications.list({ token: userToken, start: 0, count: 2 })
42
43 expect(data).to.have.lengthOf(2)
44 expect(total).to.equal(10)
45 })
46 })
47
48 describe('Mark as read', function () {
49
50 it('Should mark as read some notifications', async function () {
51 const { data } = await server.notifications.list({ token: userToken, start: 2, count: 3 })
52 const ids = data.map(n => n.id)
53
54 await server.notifications.markAsRead({ token: userToken, ids })
55 })
56
57 it('Should have the notifications marked as read', async function () {
58 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10 })
59
60 expect(data[0].read).to.be.false
61 expect(data[1].read).to.be.false
62 expect(data[2].read).to.be.true
63 expect(data[3].read).to.be.true
64 expect(data[4].read).to.be.true
65 expect(data[5].read).to.be.false
66 })
67
68 it('Should only list read notifications', async function () {
69 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: false })
70
71 for (const notification of data) {
72 expect(notification.read).to.be.true
73 }
74 })
75
76 it('Should only list unread notifications', async function () {
77 const { data } = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: true })
78
79 for (const notification of data) {
80 expect(notification.read).to.be.false
81 }
82 })
83
84 it('Should mark as read all notifications', async function () {
85 await server.notifications.markAsReadAll({ token: userToken })
86
87 const body = await server.notifications.list({ token: userToken, start: 0, count: 10, unread: true })
88
89 expect(body.total).to.equal(0)
90 expect(body.data).to.have.lengthOf(0)
91 })
92 })
93
94 describe('Notification settings', function () {
95 let baseParams: CheckerBaseParams
96
97 before(() => {
98 baseParams = {
99 server,
100 emails,
101 socketNotifications: userNotifications,
102 token: userToken
103 }
104 })
105
106 it('Should not have notifications', async function () {
107 this.timeout(40000)
108
109 await server.notifications.updateMySettings({
110 token: userToken,
111 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.NONE }
112 })
113
114 {
115 const info = await server.users.getMyInfo({ token: userToken })
116 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.NONE)
117 }
118
119 const { name, shortUUID } = await server.videos.randomUpload()
120
121 const check = { web: true, mail: true }
122 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
123 })
124
125 it('Should only have web notifications', async function () {
126 this.timeout(20000)
127
128 await server.notifications.updateMySettings({
129 token: userToken,
130 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.WEB }
131 })
132
133 {
134 const info = await server.users.getMyInfo({ token: userToken })
135 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.WEB)
136 }
137
138 const { name, shortUUID } = await server.videos.randomUpload()
139
140 {
141 const check = { mail: true, web: false }
142 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
143 }
144
145 {
146 const check = { mail: false, web: true }
147 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'presence' })
148 }
149 })
150
151 it('Should only have mail notifications', async function () {
152 this.timeout(20000)
153
154 await server.notifications.updateMySettings({
155 token: userToken,
156 settings: { ...getAllNotificationsSettings(), newVideoFromSubscription: UserNotificationSettingValue.EMAIL }
157 })
158
159 {
160 const info = await server.users.getMyInfo({ token: userToken })
161 expect(info.notificationSettings.newVideoFromSubscription).to.equal(UserNotificationSettingValue.EMAIL)
162 }
163
164 const { name, shortUUID } = await server.videos.randomUpload()
165
166 {
167 const check = { mail: false, web: true }
168 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'absence' })
169 }
170
171 {
172 const check = { mail: true, web: false }
173 await checkNewVideoFromSubscription({ ...baseParams, check, videoName: name, shortUUID, checkType: 'presence' })
174 }
175 })
176
177 it('Should have email and web notifications', async function () {
178 this.timeout(20000)
179
180 await server.notifications.updateMySettings({
181 token: userToken,
182 settings: {
183 ...getAllNotificationsSettings(),
184 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
185 }
186 })
187
188 {
189 const info = await server.users.getMyInfo({ token: userToken })
190 expect(info.notificationSettings.newVideoFromSubscription).to.equal(
191 UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
192 )
193 }
194
195 const { name, shortUUID } = await server.videos.randomUpload()
196
197 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
198 })
199 })
200
201 after(async function () {
202 MockSmtpServer.Instance.kill()
203
204 await cleanupTests([ server ])
205 })
206})
diff --git a/packages/tests/src/api/notifications/registrations-notifications.ts b/packages/tests/src/api/notifications/registrations-notifications.ts
new file mode 100644
index 000000000..1f166cb36
--- /dev/null
+++ b/packages/tests/src/api/notifications/registrations-notifications.ts
@@ -0,0 +1,83 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { UserNotification } from '@peertube/peertube-models'
4import { cleanupTests, PeerTubeServer, waitJobs } from '@peertube/peertube-server-commands'
5import { MockSmtpServer } from '@tests/shared/mock-servers/mock-email.js'
6import { CheckerBaseParams, prepareNotificationsTest, checkUserRegistered, checkRegistrationRequest } from '@tests/shared/notifications.js'
7
8describe('Test registrations notifications', function () {
9 let server: PeerTubeServer
10 let userToken1: string
11
12 let userNotifications: UserNotification[] = []
13 let adminNotifications: UserNotification[] = []
14 let emails: object[] = []
15
16 let baseParams: CheckerBaseParams
17
18 before(async function () {
19 this.timeout(120000)
20
21 const res = await prepareNotificationsTest(1)
22
23 server = res.servers[0]
24 emails = res.emails
25 userToken1 = res.userAccessToken
26 adminNotifications = res.adminNotifications
27 userNotifications = res.userNotifications
28
29 baseParams = {
30 server,
31 emails,
32 socketNotifications: adminNotifications,
33 token: server.accessToken
34 }
35 })
36
37 describe('New direct registration for moderators', function () {
38
39 before(async function () {
40 await server.config.enableSignup(false)
41 })
42
43 it('Should send a notification only to moderators when a user registers on the instance', async function () {
44 this.timeout(50000)
45
46 await server.registrations.register({ username: 'user_10' })
47
48 await waitJobs([ server ])
49
50 await checkUserRegistered({ ...baseParams, username: 'user_10', checkType: 'presence' })
51
52 const userOverride = { socketNotifications: userNotifications, token: userToken1, check: { web: true, mail: false } }
53 await checkUserRegistered({ ...baseParams, ...userOverride, username: 'user_10', checkType: 'absence' })
54 })
55 })
56
57 describe('New registration request for moderators', function () {
58
59 before(async function () {
60 await server.config.enableSignup(true)
61 })
62
63 it('Should send a notification on new registration request', async function () {
64 this.timeout(50000)
65
66 const registrationReason = 'my reason'
67 await server.registrations.requestRegistration({ username: 'user_11', registrationReason })
68
69 await waitJobs([ server ])
70
71 await checkRegistrationRequest({ ...baseParams, username: 'user_11', registrationReason, checkType: 'presence' })
72
73 const userOverride = { socketNotifications: userNotifications, token: userToken1, check: { web: true, mail: false } }
74 await checkRegistrationRequest({ ...baseParams, ...userOverride, username: 'user_11', registrationReason, checkType: 'absence' })
75 })
76 })
77
78 after(async function () {
79 MockSmtpServer.Instance.kill()
80
81 await cleanupTests([ server ])
82 })
83})
diff --git a/packages/tests/src/api/notifications/user-notifications.ts b/packages/tests/src/api/notifications/user-notifications.ts
new file mode 100644
index 000000000..4c03cdb47
--- /dev/null
+++ b/packages/tests/src/api/notifications/user-notifications.ts
@@ -0,0 +1,574 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { wait } from '@peertube/peertube-core-utils'
5import { UserNotification, UserNotificationType, VideoPrivacy, VideoStudioTask } from '@peertube/peertube-models'
6import { buildUUID } from '@peertube/peertube-node-utils'
7import { cleanupTests, findExternalSavedVideo, PeerTubeServer, stopFfmpeg, waitJobs } from '@peertube/peertube-server-commands'
8import { MockSmtpServer } from '@tests/shared/mock-servers/mock-email.js'
9import {
10 prepareNotificationsTest,
11 CheckerBaseParams,
12 checkNewVideoFromSubscription,
13 checkVideoIsPublished,
14 checkVideoStudioEditionIsFinished,
15 checkMyVideoImportIsFinished,
16 checkNewActorFollow
17} from '@tests/shared/notifications.js'
18import { FIXTURE_URLS } from '@tests/shared/tests.js'
19import { uploadRandomVideoOnServers } from '@tests/shared/videos.js'
20
21describe('Test user notifications', function () {
22 let servers: PeerTubeServer[] = []
23 let userAccessToken: string
24
25 let userNotifications: UserNotification[] = []
26 let adminNotifications: UserNotification[] = []
27 let adminNotificationsServer2: UserNotification[] = []
28 let emails: object[] = []
29
30 let channelId: number
31
32 before(async function () {
33 this.timeout(120000)
34
35 const res = await prepareNotificationsTest(3)
36 emails = res.emails
37 userAccessToken = res.userAccessToken
38 servers = res.servers
39 userNotifications = res.userNotifications
40 adminNotifications = res.adminNotifications
41 adminNotificationsServer2 = res.adminNotificationsServer2
42 channelId = res.channelId
43 })
44
45 describe('New video from my subscription notification', function () {
46 let baseParams: CheckerBaseParams
47
48 before(() => {
49 baseParams = {
50 server: servers[0],
51 emails,
52 socketNotifications: userNotifications,
53 token: userAccessToken
54 }
55 })
56
57 it('Should not send notifications if the user does not follow the video publisher', async function () {
58 this.timeout(50000)
59
60 await uploadRandomVideoOnServers(servers, 1)
61
62 const notification = await servers[0].notifications.getLatest({ token: userAccessToken })
63 expect(notification).to.be.undefined
64
65 expect(emails).to.have.lengthOf(0)
66 expect(userNotifications).to.have.lengthOf(0)
67 })
68
69 it('Should send a new video notification if the user follows the local video publisher', async function () {
70 this.timeout(15000)
71
72 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@' + servers[0].host })
73 await waitJobs(servers)
74
75 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
76 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
77 })
78
79 it('Should send a new video notification from a remote account', async function () {
80 this.timeout(150000) // Server 2 has transcoding enabled
81
82 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@' + servers[1].host })
83 await waitJobs(servers)
84
85 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2)
86 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
87 })
88
89 it('Should send a new video notification on a scheduled publication', async function () {
90 this.timeout(50000)
91
92 // In 2 seconds
93 const updateAt = new Date(new Date().getTime() + 2000)
94
95 const data = {
96 privacy: VideoPrivacy.PRIVATE,
97 scheduleUpdate: {
98 updateAt: updateAt.toISOString(),
99 privacy: VideoPrivacy.PUBLIC
100 }
101 }
102 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
103
104 await wait(6000)
105 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
106 })
107
108 it('Should send a new video notification on a remote scheduled publication', async function () {
109 this.timeout(100000)
110
111 // In 2 seconds
112 const updateAt = new Date(new Date().getTime() + 2000)
113
114 const data = {
115 privacy: VideoPrivacy.PRIVATE,
116 scheduleUpdate: {
117 updateAt: updateAt.toISOString(),
118 privacy: VideoPrivacy.PUBLIC
119 }
120 }
121 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
122 await waitJobs(servers)
123
124 await wait(6000)
125 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
126 })
127
128 it('Should not send a notification before the video is published', async function () {
129 this.timeout(150000)
130
131 const updateAt = new Date(new Date().getTime() + 1000000)
132
133 const data = {
134 privacy: VideoPrivacy.PRIVATE,
135 scheduleUpdate: {
136 updateAt: updateAt.toISOString(),
137 privacy: VideoPrivacy.PUBLIC
138 }
139 }
140 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
141
142 await wait(6000)
143 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
144 })
145
146 it('Should send a new video notification when a video becomes public', async function () {
147 this.timeout(50000)
148
149 const data = { privacy: VideoPrivacy.PRIVATE }
150 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
151
152 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
153
154 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
155
156 await waitJobs(servers)
157 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
158 })
159
160 it('Should send a new video notification when a remote video becomes public', async function () {
161 this.timeout(120000)
162
163 const data = { privacy: VideoPrivacy.PRIVATE }
164 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
165
166 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
167
168 await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
169
170 await waitJobs(servers)
171 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
172 })
173
174 it('Should not send a new video notification when a video becomes unlisted', async function () {
175 this.timeout(50000)
176
177 const data = { privacy: VideoPrivacy.PRIVATE }
178 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 1, data)
179
180 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
181
182 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
183 })
184
185 it('Should not send a new video notification when a remote video becomes unlisted', async function () {
186 this.timeout(100000)
187
188 const data = { privacy: VideoPrivacy.PRIVATE }
189 const { name, uuid, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
190
191 await servers[1].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
192
193 await waitJobs(servers)
194 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
195 })
196
197 it('Should send a new video notification after a video import', async function () {
198 this.timeout(100000)
199
200 const name = 'video import ' + buildUUID()
201
202 const attributes = {
203 name,
204 channelId,
205 privacy: VideoPrivacy.PUBLIC,
206 targetUrl: FIXTURE_URLS.goodVideo
207 }
208 const { video } = await servers[0].imports.importVideo({ attributes })
209
210 await waitJobs(servers)
211
212 await checkNewVideoFromSubscription({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
213 })
214 })
215
216 describe('My video is published', function () {
217 let baseParams: CheckerBaseParams
218
219 before(() => {
220 baseParams = {
221 server: servers[1],
222 emails,
223 socketNotifications: adminNotificationsServer2,
224 token: servers[1].accessToken
225 }
226 })
227
228 it('Should not send a notification if transcoding is not enabled', async function () {
229 this.timeout(50000)
230
231 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 1)
232 await waitJobs(servers)
233
234 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
235 })
236
237 it('Should not send a notification if the wait transcoding is false', async function () {
238 this.timeout(100_000)
239
240 await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: false })
241 await waitJobs(servers)
242
243 const notification = await servers[0].notifications.getLatest({ token: userAccessToken })
244 if (notification) {
245 expect(notification.type).to.not.equal(UserNotificationType.MY_VIDEO_PUBLISHED)
246 }
247 })
248
249 it('Should send a notification even if the video is not transcoded in other resolutions', async function () {
250 this.timeout(100_000)
251
252 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true, fixture: 'video_short_240p.mp4' })
253 await waitJobs(servers)
254
255 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
256 })
257
258 it('Should send a notification with a transcoded video', async function () {
259 this.timeout(100_000)
260
261 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
262 await waitJobs(servers)
263
264 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
265 })
266
267 it('Should send a notification when an imported video is transcoded', async function () {
268 this.timeout(120000)
269
270 const name = 'video import ' + buildUUID()
271
272 const attributes = {
273 name,
274 channelId,
275 privacy: VideoPrivacy.PUBLIC,
276 targetUrl: FIXTURE_URLS.goodVideo,
277 waitTranscoding: true
278 }
279 const { video } = await servers[1].imports.importVideo({ attributes })
280
281 await waitJobs(servers)
282 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID: video.shortUUID, checkType: 'presence' })
283 })
284
285 it('Should send a notification when the scheduled update has been proceeded', async function () {
286 this.timeout(70000)
287
288 // In 2 seconds
289 const updateAt = new Date(new Date().getTime() + 2000)
290
291 const data = {
292 privacy: VideoPrivacy.PRIVATE,
293 scheduleUpdate: {
294 updateAt: updateAt.toISOString(),
295 privacy: VideoPrivacy.PUBLIC
296 }
297 }
298 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
299
300 await wait(6000)
301 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
302 })
303
304 it('Should not send a notification before the video is published', async function () {
305 this.timeout(150000)
306
307 const updateAt = new Date(new Date().getTime() + 1000000)
308
309 const data = {
310 privacy: VideoPrivacy.PRIVATE,
311 scheduleUpdate: {
312 updateAt: updateAt.toISOString(),
313 privacy: VideoPrivacy.PUBLIC
314 }
315 }
316 const { name, shortUUID } = await uploadRandomVideoOnServers(servers, 2, data)
317
318 await wait(6000)
319 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'absence' })
320 })
321 })
322
323 describe('My live replay is published', function () {
324
325 let baseParams: CheckerBaseParams
326
327 before(() => {
328 baseParams = {
329 server: servers[1],
330 emails,
331 socketNotifications: adminNotificationsServer2,
332 token: servers[1].accessToken
333 }
334 })
335
336 it('Should send a notification is a live replay of a non permanent live is published', async function () {
337 this.timeout(120000)
338
339 const { shortUUID } = await servers[1].live.create({
340 fields: {
341 name: 'non permanent live',
342 privacy: VideoPrivacy.PUBLIC,
343 channelId: servers[1].store.channel.id,
344 saveReplay: true,
345 replaySettings: { privacy: VideoPrivacy.PUBLIC },
346 permanentLive: false
347 }
348 })
349
350 const ffmpegCommand = await servers[1].live.sendRTMPStreamInVideo({ videoId: shortUUID })
351
352 await waitJobs(servers)
353 await servers[1].live.waitUntilPublished({ videoId: shortUUID })
354
355 await stopFfmpeg(ffmpegCommand)
356 await servers[1].live.waitUntilReplacedByReplay({ videoId: shortUUID })
357
358 await waitJobs(servers)
359 await checkVideoIsPublished({ ...baseParams, videoName: 'non permanent live', shortUUID, checkType: 'presence' })
360 })
361
362 it('Should send a notification is a live replay of a permanent live is published', async function () {
363 this.timeout(120000)
364
365 const { shortUUID } = await servers[1].live.create({
366 fields: {
367 name: 'permanent live',
368 privacy: VideoPrivacy.PUBLIC,
369 channelId: servers[1].store.channel.id,
370 saveReplay: true,
371 replaySettings: { privacy: VideoPrivacy.PUBLIC },
372 permanentLive: true
373 }
374 })
375
376 const ffmpegCommand = await servers[1].live.sendRTMPStreamInVideo({ videoId: shortUUID })
377
378 await waitJobs(servers)
379 await servers[1].live.waitUntilPublished({ videoId: shortUUID })
380
381 const liveDetails = await servers[1].videos.get({ id: shortUUID })
382
383 await stopFfmpeg(ffmpegCommand)
384
385 await servers[1].live.waitUntilWaiting({ videoId: shortUUID })
386 await waitJobs(servers)
387
388 const video = await findExternalSavedVideo(servers[1], liveDetails)
389 expect(video).to.exist
390
391 await checkVideoIsPublished({ ...baseParams, videoName: video.name, shortUUID: video.shortUUID, checkType: 'presence' })
392 })
393 })
394
395 describe('Video studio', function () {
396 let baseParams: CheckerBaseParams
397
398 before(() => {
399 baseParams = {
400 server: servers[1],
401 emails,
402 socketNotifications: adminNotificationsServer2,
403 token: servers[1].accessToken
404 }
405 })
406
407 it('Should send a notification after studio edition', async function () {
408 this.timeout(240000)
409
410 const { name, shortUUID, id } = await uploadRandomVideoOnServers(servers, 2, { waitTranscoding: true })
411
412 await waitJobs(servers)
413 await checkVideoIsPublished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
414
415 const tasks: VideoStudioTask[] = [
416 {
417 name: 'cut',
418 options: {
419 start: 0,
420 end: 1
421 }
422 }
423 ]
424 await servers[1].videoStudio.createEditionTasks({ videoId: id, tasks })
425 await waitJobs(servers)
426
427 await checkVideoStudioEditionIsFinished({ ...baseParams, videoName: name, shortUUID, checkType: 'presence' })
428 })
429 })
430
431 describe('My video is imported', function () {
432 let baseParams: CheckerBaseParams
433
434 before(() => {
435 baseParams = {
436 server: servers[0],
437 emails,
438 socketNotifications: adminNotifications,
439 token: servers[0].accessToken
440 }
441 })
442
443 it('Should send a notification when the video import failed', async function () {
444 this.timeout(70000)
445
446 const name = 'video import ' + buildUUID()
447
448 const attributes = {
449 name,
450 channelId,
451 privacy: VideoPrivacy.PRIVATE,
452 targetUrl: FIXTURE_URLS.badVideo
453 }
454 const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
455
456 await waitJobs(servers)
457
458 const url = FIXTURE_URLS.badVideo
459 await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: false, checkType: 'presence' })
460 })
461
462 it('Should send a notification when the video import succeeded', async function () {
463 this.timeout(70000)
464
465 const name = 'video import ' + buildUUID()
466
467 const attributes = {
468 name,
469 channelId,
470 privacy: VideoPrivacy.PRIVATE,
471 targetUrl: FIXTURE_URLS.goodVideo
472 }
473 const { video: { shortUUID } } = await servers[0].imports.importVideo({ attributes })
474
475 await waitJobs(servers)
476
477 const url = FIXTURE_URLS.goodVideo
478 await checkMyVideoImportIsFinished({ ...baseParams, videoName: name, shortUUID, url, success: true, checkType: 'presence' })
479 })
480 })
481
482 describe('New actor follow', function () {
483 let baseParams: CheckerBaseParams
484 const myChannelName = 'super channel name'
485 const myUserName = 'super user name'
486
487 before(async function () {
488 baseParams = {
489 server: servers[0],
490 emails,
491 socketNotifications: userNotifications,
492 token: userAccessToken
493 }
494
495 await servers[0].users.updateMe({ displayName: 'super root name' })
496
497 await servers[0].users.updateMe({
498 token: userAccessToken,
499 displayName: myUserName
500 })
501
502 await servers[1].users.updateMe({ displayName: 'super root 2 name' })
503
504 await servers[0].channels.update({
505 token: userAccessToken,
506 channelName: 'user_1_channel',
507 attributes: { displayName: myChannelName }
508 })
509 })
510
511 it('Should notify when a local channel is following one of our channel', async function () {
512 this.timeout(50000)
513
514 await servers[0].subscriptions.add({ targetUri: 'user_1_channel@' + servers[0].host })
515 await waitJobs(servers)
516
517 await checkNewActorFollow({
518 ...baseParams,
519 followType: 'channel',
520 followerName: 'root',
521 followerDisplayName: 'super root name',
522 followingDisplayName: myChannelName,
523 checkType: 'presence'
524 })
525
526 await servers[0].subscriptions.remove({ uri: 'user_1_channel@' + servers[0].host })
527 })
528
529 it('Should notify when a remote channel is following one of our channel', async function () {
530 this.timeout(50000)
531
532 await servers[1].subscriptions.add({ targetUri: 'user_1_channel@' + servers[0].host })
533 await waitJobs(servers)
534
535 await checkNewActorFollow({
536 ...baseParams,
537 followType: 'channel',
538 followerName: 'root',
539 followerDisplayName: 'super root 2 name',
540 followingDisplayName: myChannelName,
541 checkType: 'presence'
542 })
543
544 await servers[1].subscriptions.remove({ uri: 'user_1_channel@' + servers[0].host })
545 })
546
547 // PeerTube does not support account -> account follows
548 // it('Should notify when a local account is following one of our channel', async function () {
549 // this.timeout(50000)
550 //
551 // await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@' + servers[0].host)
552 //
553 // await waitJobs(servers)
554 //
555 // await checkNewActorFollow(baseParams, 'account', 'root', 'super root name', myUserName, 'presence')
556 // })
557
558 // it('Should notify when a remote account is following one of our channel', async function () {
559 // this.timeout(50000)
560 //
561 // await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@' + servers[0].host)
562 //
563 // await waitJobs(servers)
564 //
565 // await checkNewActorFollow(baseParams, 'account', 'root', 'super root 2 name', myUserName, 'presence')
566 // })
567 })
568
569 after(async function () {
570 MockSmtpServer.Instance.kill()
571
572 await cleanupTests(servers)
573 })
574})