aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests/api/users
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-07-31 14:34:36 +0200
committerChocobozzz <me@florianbigard.com>2023-08-11 15:02:33 +0200
commit3a4992633ee62d5edfbb484d9c6bcb3cf158489d (patch)
treee4510b39bdac9c318fdb4b47018d08f15368b8f0 /server/tests/api/users
parent04d1da5621d25d59bd5fa1543b725c497bf5d9a8 (diff)
downloadPeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.gz
PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.zst
PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.zip
Migrate server to ESM
Sorry for the very big commit that may lead to git log issues and merge conflicts, but it's a major step forward: * Server can be faster at startup because imports() are async and we can easily lazy import big modules * Angular doesn't seem to support ES import (with .js extension), so we had to correctly organize peertube into a monorepo: * Use yarn workspace feature * Use typescript reference projects for dependencies * Shared projects have been moved into "packages", each one is now a node module (with a dedicated package.json/tsconfig.json) * server/tools have been moved into apps/ and is now a dedicated app bundled and published on NPM so users don't have to build peertube cli tools manually * server/tests have been moved into packages/ so we don't compile them every time we want to run the server * Use isolatedModule option: * Had to move from const enum to const (https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums) * Had to explictely specify "type" imports when used in decorators * Prefer tsx (that uses esbuild under the hood) instead of ts-node to load typescript files (tests with mocha or scripts): * To reduce test complexity as esbuild doesn't support decorator metadata, we only test server files that do not import server models * We still build tests files into js files for a faster CI * Remove unmaintained peertube CLI import script * Removed some barrels to speed up execution (less imports)
Diffstat (limited to 'server/tests/api/users')
-rw-r--r--server/tests/api/users/index.ts8
-rw-r--r--server/tests/api/users/oauth.ts197
-rw-r--r--server/tests/api/users/registrations.ts415
-rw-r--r--server/tests/api/users/two-factor.ts200
-rw-r--r--server/tests/api/users/user-subscriptions.ts614
-rw-r--r--server/tests/api/users/user-videos.ts219
-rw-r--r--server/tests/api/users/users-email-verification.ts165
-rw-r--r--server/tests/api/users/users-multiple-servers.ts216
-rw-r--r--server/tests/api/users/users.ts529
9 files changed, 0 insertions, 2563 deletions
diff --git a/server/tests/api/users/index.ts b/server/tests/api/users/index.ts
deleted file mode 100644
index a4443a8ec..000000000
--- a/server/tests/api/users/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
1import './oauth'
2import './registrations`'
3import './two-factor'
4import './user-subscriptions'
5import './user-videos'
6import './users'
7import './users-multiple-servers'
8import './users-email-verification'
diff --git a/server/tests/api/users/oauth.ts b/server/tests/api/users/oauth.ts
deleted file mode 100644
index 153615875..000000000
--- a/server/tests/api/users/oauth.ts
+++ /dev/null
@@ -1,197 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { SQLCommand } from '@server/tests/shared'
5import { wait } from '@shared/core-utils'
6import { HttpStatusCode, OAuth2ErrorCode, PeerTubeProblemDocument } from '@shared/models'
7import { cleanupTests, createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
8
9describe('Test oauth', function () {
10 let server: PeerTubeServer
11 let sqlCommand: SQLCommand
12
13 before(async function () {
14 this.timeout(30000)
15
16 server = await createSingleServer(1, {
17 rates_limit: {
18 login: {
19 max: 30
20 }
21 }
22 })
23
24 await setAccessTokensToServers([ server ])
25
26 sqlCommand = new SQLCommand(server)
27 })
28
29 describe('OAuth client', function () {
30
31 function expectInvalidClient (body: PeerTubeProblemDocument) {
32 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_CLIENT)
33 expect(body.error).to.contain('client is invalid')
34 expect(body.type.startsWith('https://')).to.be.true
35 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_CLIENT)
36 }
37
38 it('Should create a new client')
39
40 it('Should return the first client')
41
42 it('Should remove the last client')
43
44 it('Should not login with an invalid client id', async function () {
45 const client = { id: 'client', secret: server.store.client.secret }
46 const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
47
48 expectInvalidClient(body)
49 })
50
51 it('Should not login with an invalid client secret', async function () {
52 const client = { id: server.store.client.id, secret: 'coucou' }
53 const body = await server.login.login({ client, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
54
55 expectInvalidClient(body)
56 })
57 })
58
59 describe('Login', function () {
60
61 function expectInvalidCredentials (body: PeerTubeProblemDocument) {
62 expect(body.code).to.equal(OAuth2ErrorCode.INVALID_GRANT)
63 expect(body.error).to.contain('credentials are invalid')
64 expect(body.type.startsWith('https://')).to.be.true
65 expect(body.type).to.contain(OAuth2ErrorCode.INVALID_GRANT)
66 }
67
68 it('Should not login with an invalid username', async function () {
69 const user = { username: 'captain crochet', password: server.store.user.password }
70 const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
71
72 expectInvalidCredentials(body)
73 })
74
75 it('Should not login with an invalid password', async function () {
76 const user = { username: server.store.user.username, password: 'mew_three' }
77 const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
78
79 expectInvalidCredentials(body)
80 })
81
82 it('Should be able to login', async function () {
83 await server.login.login({ expectedStatus: HttpStatusCode.OK_200 })
84 })
85
86 it('Should be able to login with an insensitive username', async function () {
87 const user = { username: 'RoOt', password: server.store.user.password }
88 await server.login.login({ user, expectedStatus: HttpStatusCode.OK_200 })
89
90 const user2 = { username: 'rOoT', password: server.store.user.password }
91 await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 })
92
93 const user3 = { username: 'ROOt', password: server.store.user.password }
94 await server.login.login({ user: user3, expectedStatus: HttpStatusCode.OK_200 })
95 })
96 })
97
98 describe('Logout', function () {
99
100 it('Should logout (revoke token)', async function () {
101 await server.login.logout({ token: server.accessToken })
102 })
103
104 it('Should not be able to get the user information', async function () {
105 await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
106 })
107
108 it('Should not be able to upload a video', async function () {
109 await server.videos.upload({ attributes: { name: 'video' }, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
110 })
111
112 it('Should be able to login again', async function () {
113 const body = await server.login.login()
114 server.accessToken = body.access_token
115 server.refreshToken = body.refresh_token
116 })
117
118 it('Should be able to get my user information again', async function () {
119 await server.users.getMyInfo()
120 })
121
122 it('Should have an expired access token', async function () {
123 this.timeout(60000)
124
125 await sqlCommand.setTokenField(server.accessToken, 'accessTokenExpiresAt', new Date().toISOString())
126 await sqlCommand.setTokenField(server.accessToken, 'refreshTokenExpiresAt', new Date().toISOString())
127
128 await killallServers([ server ])
129 await server.run()
130
131 await server.users.getMyInfo({ expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
132 })
133
134 it('Should not be able to refresh an access token with an expired refresh token', async function () {
135 await server.login.refreshToken({ refreshToken: server.refreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
136 })
137
138 it('Should refresh the token', async function () {
139 this.timeout(50000)
140
141 const futureDate = new Date(new Date().getTime() + 1000 * 60).toISOString()
142 await sqlCommand.setTokenField(server.accessToken, 'refreshTokenExpiresAt', futureDate)
143
144 await killallServers([ server ])
145 await server.run()
146
147 const res = await server.login.refreshToken({ refreshToken: server.refreshToken })
148 server.accessToken = res.body.access_token
149 server.refreshToken = res.body.refresh_token
150 })
151
152 it('Should be able to get my user information again', async function () {
153 await server.users.getMyInfo()
154 })
155 })
156
157 describe('Custom token lifetime', function () {
158 before(async function () {
159 this.timeout(120_000)
160
161 await server.kill()
162 await server.run({
163 oauth2: {
164 token_lifetime: {
165 access_token: '2 seconds',
166 refresh_token: '2 seconds'
167 }
168 }
169 })
170 })
171
172 it('Should have a very short access token lifetime', async function () {
173 this.timeout(50000)
174
175 const { access_token: accessToken } = await server.login.login()
176 await server.users.getMyInfo({ token: accessToken })
177
178 await wait(3000)
179 await server.users.getMyInfo({ token: accessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
180 })
181
182 it('Should have a very short refresh token lifetime', async function () {
183 this.timeout(50000)
184
185 const { refresh_token: refreshToken } = await server.login.login()
186 await server.login.refreshToken({ refreshToken })
187
188 await wait(3000)
189 await server.login.refreshToken({ refreshToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
190 })
191 })
192
193 after(async function () {
194 await sqlCommand.cleanup()
195 await cleanupTests([ server ])
196 })
197})
diff --git a/server/tests/api/users/registrations.ts b/server/tests/api/users/registrations.ts
deleted file mode 100644
index e6524f07d..000000000
--- a/server/tests/api/users/registrations.ts
+++ /dev/null
@@ -1,415 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { MockSmtpServer } from '@server/tests/shared'
5import { UserRegistrationState, UserRole } from '@shared/models'
6import {
7 cleanupTests,
8 ConfigCommand,
9 createSingleServer,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 waitJobs
13} from '@shared/server-commands'
14
15describe('Test registrations', function () {
16 let server: PeerTubeServer
17
18 const emails: object[] = []
19 let emailPort: number
20
21 before(async function () {
22 this.timeout(30000)
23
24 emailPort = await MockSmtpServer.Instance.collectEmails(emails)
25
26 server = await createSingleServer(1, ConfigCommand.getEmailOverrideConfig(emailPort))
27
28 await setAccessTokensToServers([ server ])
29 await server.config.enableSignup(false)
30 })
31
32 describe('Direct registrations of a new user', function () {
33 let user1Token: string
34
35 it('Should register a new user', async function () {
36 const user = { displayName: 'super user 1', username: 'user_1', password: 'my super password' }
37 const channel = { name: 'my_user_1_channel', displayName: 'my channel rocks' }
38
39 await server.registrations.register({ ...user, channel })
40 })
41
42 it('Should be able to login with this registered user', async function () {
43 const user1 = { username: 'user_1', password: 'my super password' }
44
45 user1Token = await server.login.getAccessToken(user1)
46 })
47
48 it('Should have the correct display name', async function () {
49 const user = await server.users.getMyInfo({ token: user1Token })
50 expect(user.account.displayName).to.equal('super user 1')
51 })
52
53 it('Should have the correct video quota', async function () {
54 const user = await server.users.getMyInfo({ token: user1Token })
55 expect(user.videoQuota).to.equal(5 * 1024 * 1024)
56 })
57
58 it('Should have created the channel', async function () {
59 const { displayName } = await server.channels.get({ channelName: 'my_user_1_channel' })
60
61 expect(displayName).to.equal('my channel rocks')
62 })
63
64 it('Should remove me', async function () {
65 {
66 const { data } = await server.users.list()
67 expect(data.find(u => u.username === 'user_1')).to.not.be.undefined
68 }
69
70 await server.users.deleteMe({ token: user1Token })
71
72 {
73 const { data } = await server.users.list()
74 expect(data.find(u => u.username === 'user_1')).to.be.undefined
75 }
76 })
77 })
78
79 describe('Registration requests', function () {
80 let id2: number
81 let id3: number
82 let id4: number
83
84 let user2Token: string
85 let user3Token: string
86
87 before(async function () {
88 this.timeout(60000)
89
90 await server.config.enableSignup(true)
91
92 {
93 const { id } = await server.registrations.requestRegistration({
94 username: 'user4',
95 registrationReason: 'registration reason 4'
96 })
97
98 id4 = id
99 }
100 })
101
102 it('Should request a registration without a channel', async function () {
103 {
104 const { id } = await server.registrations.requestRegistration({
105 username: 'user2',
106 displayName: 'my super user 2',
107 email: 'user2@example.com',
108 password: 'user2password',
109 registrationReason: 'registration reason 2'
110 })
111
112 id2 = id
113 }
114 })
115
116 it('Should request a registration with a channel', async function () {
117 const { id } = await server.registrations.requestRegistration({
118 username: 'user3',
119 displayName: 'my super user 3',
120 channel: {
121 displayName: 'my user 3 channel',
122 name: 'super_user3_channel'
123 },
124 email: 'user3@example.com',
125 password: 'user3password',
126 registrationReason: 'registration reason 3'
127 })
128
129 id3 = id
130 })
131
132 it('Should list these registration requests', async function () {
133 {
134 const { total, data } = await server.registrations.list({ sort: '-createdAt' })
135 expect(total).to.equal(3)
136 expect(data).to.have.lengthOf(3)
137
138 {
139 expect(data[0].id).to.equal(id3)
140 expect(data[0].username).to.equal('user3')
141 expect(data[0].accountDisplayName).to.equal('my super user 3')
142
143 expect(data[0].channelDisplayName).to.equal('my user 3 channel')
144 expect(data[0].channelHandle).to.equal('super_user3_channel')
145
146 expect(data[0].createdAt).to.exist
147 expect(data[0].updatedAt).to.exist
148
149 expect(data[0].email).to.equal('user3@example.com')
150 expect(data[0].emailVerified).to.be.null
151
152 expect(data[0].moderationResponse).to.be.null
153 expect(data[0].registrationReason).to.equal('registration reason 3')
154 expect(data[0].state.id).to.equal(UserRegistrationState.PENDING)
155 expect(data[0].state.label).to.equal('Pending')
156 expect(data[0].user).to.be.null
157 }
158
159 {
160 expect(data[1].id).to.equal(id2)
161 expect(data[1].username).to.equal('user2')
162 expect(data[1].accountDisplayName).to.equal('my super user 2')
163
164 expect(data[1].channelDisplayName).to.be.null
165 expect(data[1].channelHandle).to.be.null
166
167 expect(data[1].createdAt).to.exist
168 expect(data[1].updatedAt).to.exist
169
170 expect(data[1].email).to.equal('user2@example.com')
171 expect(data[1].emailVerified).to.be.null
172
173 expect(data[1].moderationResponse).to.be.null
174 expect(data[1].registrationReason).to.equal('registration reason 2')
175 expect(data[1].state.id).to.equal(UserRegistrationState.PENDING)
176 expect(data[1].state.label).to.equal('Pending')
177 expect(data[1].user).to.be.null
178 }
179
180 {
181 expect(data[2].username).to.equal('user4')
182 }
183 }
184
185 {
186 const { total, data } = await server.registrations.list({ count: 1, start: 1, sort: 'createdAt' })
187
188 expect(total).to.equal(3)
189 expect(data).to.have.lengthOf(1)
190 expect(data[0].id).to.equal(id2)
191 }
192
193 {
194 const { total, data } = await server.registrations.list({ search: 'user3' })
195 expect(total).to.equal(1)
196 expect(data).to.have.lengthOf(1)
197 expect(data[0].id).to.equal(id3)
198 }
199 })
200
201 it('Should reject a registration request', async function () {
202 await server.registrations.reject({ id: id4, moderationResponse: 'I do not want id 4 on this instance' })
203 })
204
205 it('Should have sent an email to the user explanining the registration has been rejected', async function () {
206 this.timeout(50000)
207
208 await waitJobs([ server ])
209
210 const email = emails.find(e => e['to'][0]['address'] === 'user4@example.com')
211 expect(email).to.exist
212
213 expect(email['subject']).to.contain('been rejected')
214 expect(email['text']).to.contain('been rejected')
215 expect(email['text']).to.contain('I do not want id 4 on this instance')
216 })
217
218 it('Should accept registration requests', async function () {
219 await server.registrations.accept({ id: id2, moderationResponse: 'Welcome id 2' })
220 await server.registrations.accept({ id: id3, moderationResponse: 'Welcome id 3' })
221 })
222
223 it('Should have sent an email to the user explanining the registration has been accepted', async function () {
224 this.timeout(50000)
225
226 await waitJobs([ server ])
227
228 {
229 const email = emails.find(e => e['to'][0]['address'] === 'user2@example.com')
230 expect(email).to.exist
231
232 expect(email['subject']).to.contain('been accepted')
233 expect(email['text']).to.contain('been accepted')
234 expect(email['text']).to.contain('Welcome id 2')
235 }
236
237 {
238 const email = emails.find(e => e['to'][0]['address'] === 'user3@example.com')
239 expect(email).to.exist
240
241 expect(email['subject']).to.contain('been accepted')
242 expect(email['text']).to.contain('been accepted')
243 expect(email['text']).to.contain('Welcome id 3')
244 }
245 })
246
247 it('Should login with these users', async function () {
248 user2Token = await server.login.getAccessToken({ username: 'user2', password: 'user2password' })
249 user3Token = await server.login.getAccessToken({ username: 'user3', password: 'user3password' })
250 })
251
252 it('Should have created the appropriate attributes for user 2', async function () {
253 const me = await server.users.getMyInfo({ token: user2Token })
254
255 expect(me.username).to.equal('user2')
256 expect(me.account.displayName).to.equal('my super user 2')
257 expect(me.videoQuota).to.equal(5 * 1024 * 1024)
258 expect(me.videoChannels[0].name).to.equal('user2_channel')
259 expect(me.videoChannels[0].displayName).to.equal('Main user2 channel')
260 expect(me.role.id).to.equal(UserRole.USER)
261 expect(me.email).to.equal('user2@example.com')
262 })
263
264 it('Should have created the appropriate attributes for user 3', async function () {
265 const me = await server.users.getMyInfo({ token: user3Token })
266
267 expect(me.username).to.equal('user3')
268 expect(me.account.displayName).to.equal('my super user 3')
269 expect(me.videoQuota).to.equal(5 * 1024 * 1024)
270 expect(me.videoChannels[0].name).to.equal('super_user3_channel')
271 expect(me.videoChannels[0].displayName).to.equal('my user 3 channel')
272 expect(me.role.id).to.equal(UserRole.USER)
273 expect(me.email).to.equal('user3@example.com')
274 })
275
276 it('Should list these accepted/rejected registration requests', async function () {
277 const { data } = await server.registrations.list({ sort: 'createdAt' })
278 const { data: users } = await server.users.list()
279
280 {
281 expect(data[0].id).to.equal(id4)
282 expect(data[0].state.id).to.equal(UserRegistrationState.REJECTED)
283 expect(data[0].state.label).to.equal('Rejected')
284
285 expect(data[0].moderationResponse).to.equal('I do not want id 4 on this instance')
286 expect(data[0].user).to.be.null
287
288 expect(users.find(u => u.username === 'user4')).to.not.exist
289 }
290
291 {
292 expect(data[1].id).to.equal(id2)
293 expect(data[1].state.id).to.equal(UserRegistrationState.ACCEPTED)
294 expect(data[1].state.label).to.equal('Accepted')
295
296 expect(data[1].moderationResponse).to.equal('Welcome id 2')
297 expect(data[1].user).to.exist
298
299 const user2 = users.find(u => u.username === 'user2')
300 expect(data[1].user.id).to.equal(user2.id)
301 }
302
303 {
304 expect(data[2].id).to.equal(id3)
305 expect(data[2].state.id).to.equal(UserRegistrationState.ACCEPTED)
306 expect(data[2].state.label).to.equal('Accepted')
307
308 expect(data[2].moderationResponse).to.equal('Welcome id 3')
309 expect(data[2].user).to.exist
310
311 const user3 = users.find(u => u.username === 'user3')
312 expect(data[2].user.id).to.equal(user3.id)
313 }
314 })
315
316 it('Shoulde delete a registration', async function () {
317 await server.registrations.delete({ id: id2 })
318 await server.registrations.delete({ id: id3 })
319
320 const { total, data } = await server.registrations.list()
321 expect(total).to.equal(1)
322 expect(data).to.have.lengthOf(1)
323 expect(data[0].id).to.equal(id4)
324
325 const { data: users } = await server.users.list()
326
327 for (const username of [ 'user2', 'user3' ]) {
328 expect(users.find(u => u.username === username)).to.exist
329 }
330 })
331
332 it('Should be able to prevent email delivery on accept/reject', async function () {
333 this.timeout(50000)
334
335 let id1: number
336 let id2: number
337
338 {
339 const { id } = await server.registrations.requestRegistration({
340 username: 'user7',
341 email: 'user7@example.com',
342 registrationReason: 'tt'
343 })
344 id1 = id
345 }
346 {
347 const { id } = await server.registrations.requestRegistration({
348 username: 'user8',
349 email: 'user8@example.com',
350 registrationReason: 'tt'
351 })
352 id2 = id
353 }
354
355 await server.registrations.accept({ id: id1, moderationResponse: 'tt', preventEmailDelivery: true })
356 await server.registrations.reject({ id: id2, moderationResponse: 'tt', preventEmailDelivery: true })
357
358 await waitJobs([ server ])
359
360 const filtered = emails.filter(e => {
361 const address = e['to'][0]['address']
362 return address === 'user7@example.com' || address === 'user8@example.com'
363 })
364
365 expect(filtered).to.have.lengthOf(0)
366 })
367
368 it('Should request a registration without a channel, that will conflict with an already existing channel', async function () {
369 let id1: number
370 let id2: number
371
372 {
373 const { id } = await server.registrations.requestRegistration({
374 registrationReason: 'tt',
375 username: 'user5',
376 password: 'user5password',
377 channel: {
378 displayName: 'channel 6',
379 name: 'user6_channel'
380 }
381 })
382
383 id1 = id
384 }
385
386 {
387 const { id } = await server.registrations.requestRegistration({
388 registrationReason: 'tt',
389 username: 'user6',
390 password: 'user6password'
391 })
392
393 id2 = id
394 }
395
396 await server.registrations.accept({ id: id1, moderationResponse: 'tt' })
397 await server.registrations.accept({ id: id2, moderationResponse: 'tt' })
398
399 const user5Token = await server.login.getAccessToken('user5', 'user5password')
400 const user6Token = await server.login.getAccessToken('user6', 'user6password')
401
402 const user5 = await server.users.getMyInfo({ token: user5Token })
403 const user6 = await server.users.getMyInfo({ token: user6Token })
404
405 expect(user5.videoChannels[0].name).to.equal('user6_channel')
406 expect(user6.videoChannels[0].name).to.equal('user6_channel-1')
407 })
408 })
409
410 after(async function () {
411 MockSmtpServer.Instance.kill()
412
413 await cleanupTests([ server ])
414 })
415})
diff --git a/server/tests/api/users/two-factor.ts b/server/tests/api/users/two-factor.ts
deleted file mode 100644
index 0dcab9e17..000000000
--- a/server/tests/api/users/two-factor.ts
+++ /dev/null
@@ -1,200 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { expectStartWith } from '@server/tests/shared'
5import { HttpStatusCode } from '@shared/models'
6import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, TwoFactorCommand } from '@shared/server-commands'
7
8async function login (options: {
9 server: PeerTubeServer
10 username: string
11 password: string
12 otpToken?: string
13 expectedStatus?: HttpStatusCode
14}) {
15 const { server, username, password, otpToken, expectedStatus } = options
16
17 const user = { username, password }
18 const { res, body: { access_token: token } } = await server.login.loginAndGetResponse({ user, otpToken, expectedStatus })
19
20 return { res, token }
21}
22
23describe('Test users', function () {
24 let server: PeerTubeServer
25 let otpSecret: string
26 let requestToken: string
27
28 const userUsername = 'user1'
29 let userId: number
30 let userPassword: string
31 let userToken: string
32
33 before(async function () {
34 this.timeout(30000)
35
36 server = await createSingleServer(1)
37
38 await setAccessTokensToServers([ server ])
39 const res = await server.users.generate(userUsername)
40 userId = res.userId
41 userPassword = res.password
42 userToken = res.token
43 })
44
45 it('Should not add the header on login if two factor is not enabled', async function () {
46 const { res, token } = await login({ server, username: userUsername, password: userPassword })
47
48 expect(res.header['x-peertube-otp']).to.not.exist
49
50 await server.users.getMyInfo({ token })
51 })
52
53 it('Should request two factor and get the secret and uri', async function () {
54 const { otpRequest } = await server.twoFactor.request({ userId, token: userToken, currentPassword: userPassword })
55
56 expect(otpRequest.requestToken).to.exist
57
58 expect(otpRequest.secret).to.exist
59 expect(otpRequest.secret).to.have.lengthOf(32)
60
61 expect(otpRequest.uri).to.exist
62 expectStartWith(otpRequest.uri, 'otpauth://')
63 expect(otpRequest.uri).to.include(otpRequest.secret)
64
65 requestToken = otpRequest.requestToken
66 otpSecret = otpRequest.secret
67 })
68
69 it('Should not have two factor confirmed yet', async function () {
70 const { twoFactorEnabled } = await server.users.getMyInfo({ token: userToken })
71 expect(twoFactorEnabled).to.be.false
72 })
73
74 it('Should confirm two factor', async function () {
75 await server.twoFactor.confirmRequest({
76 userId,
77 token: userToken,
78 otpToken: TwoFactorCommand.buildOTP({ secret: otpSecret }).generate(),
79 requestToken
80 })
81 })
82
83 it('Should not add the header on login if two factor is enabled and password is incorrect', async function () {
84 const { res, token } = await login({ server, username: userUsername, password: 'fake', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
85
86 expect(res.header['x-peertube-otp']).to.not.exist
87 expect(token).to.not.exist
88 })
89
90 it('Should add the header on login if two factor is enabled and password is correct', async function () {
91 const { res, token } = await login({
92 server,
93 username: userUsername,
94 password: userPassword,
95 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
96 })
97
98 expect(res.header['x-peertube-otp']).to.exist
99 expect(token).to.not.exist
100
101 await server.users.getMyInfo({ token })
102 })
103
104 it('Should not login with correct password and incorrect otp secret', async function () {
105 const otp = TwoFactorCommand.buildOTP({ secret: 'a'.repeat(32) })
106
107 const { res, token } = await login({
108 server,
109 username: userUsername,
110 password: userPassword,
111 otpToken: otp.generate(),
112 expectedStatus: HttpStatusCode.BAD_REQUEST_400
113 })
114
115 expect(res.header['x-peertube-otp']).to.not.exist
116 expect(token).to.not.exist
117 })
118
119 it('Should not login with correct password and incorrect otp code', async function () {
120 const { res, token } = await login({
121 server,
122 username: userUsername,
123 password: userPassword,
124 otpToken: '123456',
125 expectedStatus: HttpStatusCode.BAD_REQUEST_400
126 })
127
128 expect(res.header['x-peertube-otp']).to.not.exist
129 expect(token).to.not.exist
130 })
131
132 it('Should not login with incorrect password and correct otp code', async function () {
133 const otpToken = TwoFactorCommand.buildOTP({ secret: otpSecret }).generate()
134
135 const { res, token } = await login({
136 server,
137 username: userUsername,
138 password: 'fake',
139 otpToken,
140 expectedStatus: HttpStatusCode.BAD_REQUEST_400
141 })
142
143 expect(res.header['x-peertube-otp']).to.not.exist
144 expect(token).to.not.exist
145 })
146
147 it('Should correctly login with correct password and otp code', async function () {
148 const otpToken = TwoFactorCommand.buildOTP({ secret: otpSecret }).generate()
149
150 const { res, token } = await login({ server, username: userUsername, password: userPassword, otpToken })
151
152 expect(res.header['x-peertube-otp']).to.not.exist
153 expect(token).to.exist
154
155 await server.users.getMyInfo({ token })
156 })
157
158 it('Should have two factor enabled when getting my info', async function () {
159 const { twoFactorEnabled } = await server.users.getMyInfo({ token: userToken })
160 expect(twoFactorEnabled).to.be.true
161 })
162
163 it('Should disable two factor and be able to login without otp token', async function () {
164 await server.twoFactor.disable({ userId, token: userToken, currentPassword: userPassword })
165
166 const { res, token } = await login({ server, username: userUsername, password: userPassword })
167 expect(res.header['x-peertube-otp']).to.not.exist
168
169 await server.users.getMyInfo({ token })
170 })
171
172 it('Should have two factor disabled when getting my info', async function () {
173 const { twoFactorEnabled } = await server.users.getMyInfo({ token: userToken })
174 expect(twoFactorEnabled).to.be.false
175 })
176
177 it('Should enable two factor auth without password from an admin', async function () {
178 const { otpRequest } = await server.twoFactor.request({ userId })
179
180 await server.twoFactor.confirmRequest({
181 userId,
182 otpToken: TwoFactorCommand.buildOTP({ secret: otpRequest.secret }).generate(),
183 requestToken: otpRequest.requestToken
184 })
185
186 const { twoFactorEnabled } = await server.users.getMyInfo({ token: userToken })
187 expect(twoFactorEnabled).to.be.true
188 })
189
190 it('Should disable two factor auth without password from an admin', async function () {
191 await server.twoFactor.disable({ userId })
192
193 const { twoFactorEnabled } = await server.users.getMyInfo({ token: userToken })
194 expect(twoFactorEnabled).to.be.false
195 })
196
197 after(async function () {
198 await cleanupTests([ server ])
199 })
200})
diff --git a/server/tests/api/users/user-subscriptions.ts b/server/tests/api/users/user-subscriptions.ts
deleted file mode 100644
index ad2b82a4a..000000000
--- a/server/tests/api/users/user-subscriptions.ts
+++ /dev/null
@@ -1,614 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { VideoPrivacy } from '@shared/models'
5import {
6 cleanupTests,
7 createMultipleServers,
8 doubleFollow,
9 PeerTubeServer,
10 setAccessTokensToServers,
11 setDefaultAccountAvatar,
12 setDefaultChannelAvatar,
13 SubscriptionsCommand,
14 waitJobs
15} from '@shared/server-commands'
16
17describe('Test users subscriptions', function () {
18 let servers: PeerTubeServer[] = []
19 const users: { accessToken: string }[] = []
20 let video3UUID: string
21
22 let command: SubscriptionsCommand
23
24 before(async function () {
25 this.timeout(240000)
26
27 servers = await createMultipleServers(3)
28
29 // Get the access tokens
30 await setAccessTokensToServers(servers)
31 await setDefaultChannelAvatar(servers)
32 await setDefaultAccountAvatar(servers)
33
34 // Server 1 and server 2 follow each other
35 await doubleFollow(servers[0], servers[1])
36
37 for (const server of servers) {
38 const user = { username: 'user' + server.serverNumber, password: 'password' }
39 await server.users.create({ username: user.username, password: user.password })
40
41 const accessToken = await server.login.getAccessToken(user)
42 users.push({ accessToken })
43
44 const videoName1 = 'video 1-' + server.serverNumber
45 await server.videos.upload({ token: accessToken, attributes: { name: videoName1 } })
46
47 const videoName2 = 'video 2-' + server.serverNumber
48 await server.videos.upload({ token: accessToken, attributes: { name: videoName2 } })
49 }
50
51 await waitJobs(servers)
52
53 command = servers[0].subscriptions
54 })
55
56 describe('Destinction between server videos and user videos', function () {
57 it('Should display videos of server 2 on server 1', async function () {
58 const { total } = await servers[0].videos.list()
59
60 expect(total).to.equal(4)
61 })
62
63 it('User of server 1 should follow user of server 3 and root of server 1', async function () {
64 this.timeout(60000)
65
66 await command.add({ token: users[0].accessToken, targetUri: 'user3_channel@' + servers[2].host })
67 await command.add({ token: users[0].accessToken, targetUri: 'root_channel@' + servers[0].host })
68
69 await waitJobs(servers)
70
71 const attributes = { name: 'video server 3 added after follow' }
72 const { uuid } = await servers[2].videos.upload({ token: users[2].accessToken, attributes })
73 video3UUID = uuid
74
75 await waitJobs(servers)
76 })
77
78 it('Should not display videos of server 3 on server 1', async function () {
79 const { total, data } = await servers[0].videos.list()
80 expect(total).to.equal(4)
81
82 for (const video of data) {
83 expect(video.name).to.not.contain('1-3')
84 expect(video.name).to.not.contain('2-3')
85 expect(video.name).to.not.contain('video server 3 added after follow')
86 }
87 })
88 })
89
90 describe('Subscription endpoints', function () {
91
92 it('Should list subscriptions', async function () {
93 {
94 const body = await command.list()
95 expect(body.total).to.equal(0)
96 expect(body.data).to.be.an('array')
97 expect(body.data).to.have.lengthOf(0)
98 }
99
100 {
101 const body = await command.list({ token: users[0].accessToken, sort: 'createdAt' })
102 expect(body.total).to.equal(2)
103
104 const subscriptions = body.data
105 expect(subscriptions).to.be.an('array')
106 expect(subscriptions).to.have.lengthOf(2)
107
108 expect(subscriptions[0].name).to.equal('user3_channel')
109 expect(subscriptions[1].name).to.equal('root_channel')
110 }
111 })
112
113 it('Should get subscription', async function () {
114 {
115 const videoChannel = await command.get({ token: users[0].accessToken, uri: 'user3_channel@' + servers[2].host })
116
117 expect(videoChannel.name).to.equal('user3_channel')
118 expect(videoChannel.host).to.equal(servers[2].host)
119 expect(videoChannel.displayName).to.equal('Main user3 channel')
120 expect(videoChannel.followingCount).to.equal(0)
121 expect(videoChannel.followersCount).to.equal(1)
122 }
123
124 {
125 const videoChannel = await command.get({ token: users[0].accessToken, uri: 'root_channel@' + servers[0].host })
126
127 expect(videoChannel.name).to.equal('root_channel')
128 expect(videoChannel.host).to.equal(servers[0].host)
129 expect(videoChannel.displayName).to.equal('Main root channel')
130 expect(videoChannel.followingCount).to.equal(0)
131 expect(videoChannel.followersCount).to.equal(1)
132 }
133 })
134
135 it('Should return the existing subscriptions', async function () {
136 const uris = [
137 'user3_channel@' + servers[2].host,
138 'root2_channel@' + servers[0].host,
139 'root_channel@' + servers[0].host,
140 'user3_channel@' + servers[0].host
141 ]
142
143 const body = await command.exist({ token: users[0].accessToken, uris })
144
145 expect(body['user3_channel@' + servers[2].host]).to.be.true
146 expect(body['root2_channel@' + servers[0].host]).to.be.false
147 expect(body['root_channel@' + servers[0].host]).to.be.true
148 expect(body['user3_channel@' + servers[0].host]).to.be.false
149 })
150
151 it('Should search among subscriptions', async function () {
152 {
153 const body = await command.list({ token: users[0].accessToken, sort: '-createdAt', search: 'user3_channel' })
154 expect(body.total).to.equal(1)
155 expect(body.data).to.have.lengthOf(1)
156 }
157
158 {
159 const body = await command.list({ token: users[0].accessToken, sort: '-createdAt', search: 'toto' })
160 expect(body.total).to.equal(0)
161 expect(body.data).to.have.lengthOf(0)
162 }
163 })
164 })
165
166 describe('Subscription videos', function () {
167
168 it('Should list subscription videos', async function () {
169 {
170 const body = await servers[0].videos.listMySubscriptionVideos()
171 expect(body.total).to.equal(0)
172 expect(body.data).to.be.an('array')
173 expect(body.data).to.have.lengthOf(0)
174 }
175
176 {
177 const body = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken, sort: 'createdAt' })
178 expect(body.total).to.equal(3)
179
180 const videos = body.data
181 expect(videos).to.be.an('array')
182 expect(videos).to.have.lengthOf(3)
183
184 expect(videos[0].name).to.equal('video 1-3')
185 expect(videos[1].name).to.equal('video 2-3')
186 expect(videos[2].name).to.equal('video server 3 added after follow')
187 }
188
189 {
190 const body = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken, count: 1, start: 1 })
191 expect(body.total).to.equal(3)
192
193 const videos = body.data
194 expect(videos).to.be.an('array')
195 expect(videos).to.have.lengthOf(1)
196
197 expect(videos[0].name).to.equal('video 2-3')
198 }
199 })
200
201 it('Should upload a video by root on server 1 and see it in the subscription videos', async function () {
202 this.timeout(60000)
203
204 const videoName = 'video server 1 added after follow'
205 await servers[0].videos.upload({ attributes: { name: videoName } })
206
207 await waitJobs(servers)
208
209 {
210 const body = await servers[0].videos.listMySubscriptionVideos()
211 expect(body.total).to.equal(0)
212 expect(body.data).to.be.an('array')
213 expect(body.data).to.have.lengthOf(0)
214 }
215
216 {
217 const body = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken, sort: 'createdAt' })
218 expect(body.total).to.equal(4)
219
220 const videos = body.data
221 expect(videos).to.be.an('array')
222 expect(videos).to.have.lengthOf(4)
223
224 expect(videos[0].name).to.equal('video 1-3')
225 expect(videos[1].name).to.equal('video 2-3')
226 expect(videos[2].name).to.equal('video server 3 added after follow')
227 expect(videos[3].name).to.equal('video server 1 added after follow')
228 }
229
230 {
231 const { data, total } = await servers[0].videos.list()
232 expect(total).to.equal(5)
233
234 for (const video of data) {
235 expect(video.name).to.not.contain('1-3')
236 expect(video.name).to.not.contain('2-3')
237 expect(video.name).to.not.contain('video server 3 added after follow')
238 }
239 }
240 })
241
242 it('Should have server 1 following server 3 and display server 3 videos', async function () {
243 this.timeout(60000)
244
245 await servers[0].follows.follow({ hosts: [ servers[2].url ] })
246
247 await waitJobs(servers)
248
249 const { data, total } = await servers[0].videos.list()
250 expect(total).to.equal(8)
251
252 const names = [ '1-3', '2-3', 'video server 3 added after follow' ]
253 for (const name of names) {
254 const video = data.find(v => v.name.includes(name))
255 expect(video).to.not.be.undefined
256 }
257 })
258
259 it('Should remove follow server 1 -> server 3 and hide server 3 videos', async function () {
260 this.timeout(60000)
261
262 await servers[0].follows.unfollow({ target: servers[2] })
263
264 await waitJobs(servers)
265
266 const { total, data } = await servers[0].videos.list()
267 expect(total).to.equal(5)
268
269 for (const video of data) {
270 expect(video.name).to.not.contain('1-3')
271 expect(video.name).to.not.contain('2-3')
272 expect(video.name).to.not.contain('video server 3 added after follow')
273 }
274 })
275
276 it('Should still list subscription videos', async function () {
277 {
278 const body = await servers[0].videos.listMySubscriptionVideos()
279 expect(body.total).to.equal(0)
280 expect(body.data).to.be.an('array')
281 expect(body.data).to.have.lengthOf(0)
282 }
283
284 {
285 const body = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken, sort: 'createdAt' })
286 expect(body.total).to.equal(4)
287
288 const videos = body.data
289 expect(videos).to.be.an('array')
290 expect(videos).to.have.lengthOf(4)
291
292 expect(videos[0].name).to.equal('video 1-3')
293 expect(videos[1].name).to.equal('video 2-3')
294 expect(videos[2].name).to.equal('video server 3 added after follow')
295 expect(videos[3].name).to.equal('video server 1 added after follow')
296 }
297 })
298 })
299
300 describe('Existing subscription video update', function () {
301
302 it('Should update a video of server 3 and see the updated video on server 1', async function () {
303 this.timeout(30000)
304
305 await servers[2].videos.update({ id: video3UUID, attributes: { name: 'video server 3 added after follow updated' } })
306
307 await waitJobs(servers)
308
309 const body = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken, sort: 'createdAt' })
310 expect(body.data[2].name).to.equal('video server 3 added after follow updated')
311 })
312 })
313
314 describe('Subscription removal', function () {
315
316 it('Should remove user of server 3 subscription', async function () {
317 this.timeout(30000)
318
319 await command.remove({ token: users[0].accessToken, uri: 'user3_channel@' + servers[2].host })
320
321 await waitJobs(servers)
322 })
323
324 it('Should not display its videos anymore', async function () {
325 const body = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken, sort: 'createdAt' })
326 expect(body.total).to.equal(1)
327
328 const videos = body.data
329 expect(videos).to.be.an('array')
330 expect(videos).to.have.lengthOf(1)
331
332 expect(videos[0].name).to.equal('video server 1 added after follow')
333 })
334
335 it('Should remove the root subscription and not display the videos anymore', async function () {
336 this.timeout(30000)
337
338 await command.remove({ token: users[0].accessToken, uri: 'root_channel@' + servers[0].host })
339
340 await waitJobs(servers)
341
342 {
343 const body = await command.list({ token: users[0].accessToken, sort: 'createdAt' })
344 expect(body.total).to.equal(0)
345
346 const videos = body.data
347 expect(videos).to.be.an('array')
348 expect(videos).to.have.lengthOf(0)
349 }
350 })
351
352 it('Should correctly display public videos on server 1', async function () {
353 const { total, data } = await servers[0].videos.list()
354 expect(total).to.equal(5)
355
356 for (const video of data) {
357 expect(video.name).to.not.contain('1-3')
358 expect(video.name).to.not.contain('2-3')
359 expect(video.name).to.not.contain('video server 3 added after follow updated')
360 }
361 })
362 })
363
364 describe('Re-follow', function () {
365
366 it('Should follow user of server 3 again', async function () {
367 this.timeout(60000)
368
369 await command.add({ token: users[0].accessToken, targetUri: 'user3_channel@' + servers[2].host })
370
371 await waitJobs(servers)
372
373 {
374 const body = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken, sort: 'createdAt' })
375 expect(body.total).to.equal(3)
376
377 const videos = body.data
378 expect(videos).to.be.an('array')
379 expect(videos).to.have.lengthOf(3)
380
381 expect(videos[0].name).to.equal('video 1-3')
382 expect(videos[1].name).to.equal('video 2-3')
383 expect(videos[2].name).to.equal('video server 3 added after follow updated')
384 }
385
386 {
387 const { total, data } = await servers[0].videos.list()
388 expect(total).to.equal(5)
389
390 for (const video of data) {
391 expect(video.name).to.not.contain('1-3')
392 expect(video.name).to.not.contain('2-3')
393 expect(video.name).to.not.contain('video server 3 added after follow updated')
394 }
395 }
396 })
397
398 it('Should follow user channels of server 3 by root of server 3', async function () {
399 this.timeout(60000)
400
401 await servers[2].channels.create({ token: users[2].accessToken, attributes: { name: 'user3_channel2' } })
402
403 await servers[2].subscriptions.add({ token: servers[2].accessToken, targetUri: 'user3_channel@' + servers[2].host })
404 await servers[2].subscriptions.add({ token: servers[2].accessToken, targetUri: 'user3_channel2@' + servers[2].host })
405
406 await waitJobs(servers)
407 })
408 })
409
410 describe('Followers listing', function () {
411
412 it('Should list user 3 followers', async function () {
413 {
414 const { total, data } = await servers[2].accounts.listFollowers({
415 token: users[2].accessToken,
416 accountName: 'user3',
417 start: 0,
418 count: 5,
419 sort: 'createdAt'
420 })
421
422 expect(total).to.equal(3)
423 expect(data).to.have.lengthOf(3)
424
425 expect(data[0].following.host).to.equal(servers[2].host)
426 expect(data[0].following.name).to.equal('user3_channel')
427 expect(data[0].follower.host).to.equal(servers[0].host)
428 expect(data[0].follower.name).to.equal('user1')
429
430 expect(data[1].following.host).to.equal(servers[2].host)
431 expect(data[1].following.name).to.equal('user3_channel')
432 expect(data[1].follower.host).to.equal(servers[2].host)
433 expect(data[1].follower.name).to.equal('root')
434
435 expect(data[2].following.host).to.equal(servers[2].host)
436 expect(data[2].following.name).to.equal('user3_channel2')
437 expect(data[2].follower.host).to.equal(servers[2].host)
438 expect(data[2].follower.name).to.equal('root')
439 }
440
441 {
442 const { total, data } = await servers[2].accounts.listFollowers({
443 token: users[2].accessToken,
444 accountName: 'user3',
445 start: 0,
446 count: 1,
447 sort: '-createdAt'
448 })
449
450 expect(total).to.equal(3)
451 expect(data).to.have.lengthOf(1)
452
453 expect(data[0].following.host).to.equal(servers[2].host)
454 expect(data[0].following.name).to.equal('user3_channel2')
455 expect(data[0].follower.host).to.equal(servers[2].host)
456 expect(data[0].follower.name).to.equal('root')
457 }
458
459 {
460 const { total, data } = await servers[2].accounts.listFollowers({
461 token: users[2].accessToken,
462 accountName: 'user3',
463 start: 1,
464 count: 1,
465 sort: '-createdAt'
466 })
467
468 expect(total).to.equal(3)
469 expect(data).to.have.lengthOf(1)
470
471 expect(data[0].following.host).to.equal(servers[2].host)
472 expect(data[0].following.name).to.equal('user3_channel')
473 expect(data[0].follower.host).to.equal(servers[2].host)
474 expect(data[0].follower.name).to.equal('root')
475 }
476
477 {
478 const { total, data } = await servers[2].accounts.listFollowers({
479 token: users[2].accessToken,
480 accountName: 'user3',
481 search: 'user1',
482 sort: '-createdAt'
483 })
484
485 expect(total).to.equal(1)
486 expect(data).to.have.lengthOf(1)
487
488 expect(data[0].following.host).to.equal(servers[2].host)
489 expect(data[0].following.name).to.equal('user3_channel')
490 expect(data[0].follower.host).to.equal(servers[0].host)
491 expect(data[0].follower.name).to.equal('user1')
492 }
493 })
494
495 it('Should list user3_channel followers', async function () {
496 {
497 const { total, data } = await servers[2].channels.listFollowers({
498 token: users[2].accessToken,
499 channelName: 'user3_channel',
500 start: 0,
501 count: 5,
502 sort: 'createdAt'
503 })
504
505 expect(total).to.equal(2)
506 expect(data).to.have.lengthOf(2)
507
508 expect(data[0].following.host).to.equal(servers[2].host)
509 expect(data[0].following.name).to.equal('user3_channel')
510 expect(data[0].follower.host).to.equal(servers[0].host)
511 expect(data[0].follower.name).to.equal('user1')
512
513 expect(data[1].following.host).to.equal(servers[2].host)
514 expect(data[1].following.name).to.equal('user3_channel')
515 expect(data[1].follower.host).to.equal(servers[2].host)
516 expect(data[1].follower.name).to.equal('root')
517 }
518
519 {
520 const { total, data } = await servers[2].channels.listFollowers({
521 token: users[2].accessToken,
522 channelName: 'user3_channel',
523 start: 0,
524 count: 1,
525 sort: '-createdAt'
526 })
527
528 expect(total).to.equal(2)
529 expect(data).to.have.lengthOf(1)
530
531 expect(data[0].following.host).to.equal(servers[2].host)
532 expect(data[0].following.name).to.equal('user3_channel')
533 expect(data[0].follower.host).to.equal(servers[2].host)
534 expect(data[0].follower.name).to.equal('root')
535 }
536
537 {
538 const { total, data } = await servers[2].channels.listFollowers({
539 token: users[2].accessToken,
540 channelName: 'user3_channel',
541 start: 1,
542 count: 1,
543 sort: '-createdAt'
544 })
545
546 expect(total).to.equal(2)
547 expect(data).to.have.lengthOf(1)
548
549 expect(data[0].following.host).to.equal(servers[2].host)
550 expect(data[0].following.name).to.equal('user3_channel')
551 expect(data[0].follower.host).to.equal(servers[0].host)
552 expect(data[0].follower.name).to.equal('user1')
553 }
554
555 {
556 const { total, data } = await servers[2].channels.listFollowers({
557 token: users[2].accessToken,
558 channelName: 'user3_channel',
559 search: 'user1',
560 sort: '-createdAt'
561 })
562
563 expect(total).to.equal(1)
564 expect(data).to.have.lengthOf(1)
565
566 expect(data[0].following.host).to.equal(servers[2].host)
567 expect(data[0].following.name).to.equal('user3_channel')
568 expect(data[0].follower.host).to.equal(servers[0].host)
569 expect(data[0].follower.name).to.equal('user1')
570 }
571 })
572 })
573
574 describe('Subscription videos privacy', function () {
575
576 it('Should update video as internal and not see from remote server', async function () {
577 this.timeout(30000)
578
579 await servers[2].videos.update({ id: video3UUID, attributes: { name: 'internal', privacy: VideoPrivacy.INTERNAL } })
580 await waitJobs(servers)
581
582 {
583 const { data } = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken })
584 expect(data.find(v => v.name === 'internal')).to.not.exist
585 }
586 })
587
588 it('Should see internal from local user', async function () {
589 const { data } = await servers[2].videos.listMySubscriptionVideos({ token: servers[2].accessToken })
590 expect(data.find(v => v.name === 'internal')).to.exist
591 })
592
593 it('Should update video as private and not see from anyone server', async function () {
594 this.timeout(30000)
595
596 await servers[2].videos.update({ id: video3UUID, attributes: { name: 'private', privacy: VideoPrivacy.PRIVATE } })
597 await waitJobs(servers)
598
599 {
600 const { data } = await servers[0].videos.listMySubscriptionVideos({ token: users[0].accessToken })
601 expect(data.find(v => v.name === 'private')).to.not.exist
602 }
603
604 {
605 const { data } = await servers[2].videos.listMySubscriptionVideos({ token: servers[2].accessToken })
606 expect(data.find(v => v.name === 'private')).to.not.exist
607 }
608 })
609 })
610
611 after(async function () {
612 await cleanupTests(servers)
613 })
614})
diff --git a/server/tests/api/users/user-videos.ts b/server/tests/api/users/user-videos.ts
deleted file mode 100644
index 77226e48e..000000000
--- a/server/tests/api/users/user-videos.ts
+++ /dev/null
@@ -1,219 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { HttpStatusCode } from '@shared/models'
5import {
6 cleanupTests,
7 createSingleServer,
8 PeerTubeServer,
9 setAccessTokensToServers,
10 setDefaultAccountAvatar,
11 setDefaultChannelAvatar,
12 waitJobs
13} from '@shared/server-commands'
14
15describe('Test user videos', function () {
16 let server: PeerTubeServer
17 let videoId: number
18 let videoId2: number
19 let token: string
20 let anotherUserToken: string
21
22 before(async function () {
23 this.timeout(120000)
24
25 server = await createSingleServer(1)
26
27 await setAccessTokensToServers([ server ])
28 await setDefaultChannelAvatar([ server ])
29 await setDefaultAccountAvatar([ server ])
30
31 await server.videos.quickUpload({ name: 'root video' })
32 await server.videos.quickUpload({ name: 'root video 2' })
33
34 token = await server.users.generateUserAndToken('user')
35 anotherUserToken = await server.users.generateUserAndToken('user2')
36 })
37
38 describe('List my videos', function () {
39
40 it('Should list my videos', async function () {
41 const { data, total } = await server.videos.listMyVideos()
42
43 expect(total).to.equal(2)
44 expect(data).to.have.lengthOf(2)
45 })
46 })
47
48 describe('Upload', function () {
49
50 it('Should upload the video with the correct token', async function () {
51 await server.videos.upload({ token })
52 const { data } = await server.videos.list()
53 const video = data[0]
54
55 expect(video.account.name).to.equal('user')
56 videoId = video.id
57 })
58
59 it('Should upload the video again with the correct token', async function () {
60 const { id } = await server.videos.upload({ token })
61 videoId2 = id
62 })
63 })
64
65 describe('Ratings', function () {
66
67 it('Should retrieve a video rating', async function () {
68 await server.videos.rate({ id: videoId, token, rating: 'like' })
69 const rating = await server.users.getMyRating({ token, videoId })
70
71 expect(rating.videoId).to.equal(videoId)
72 expect(rating.rating).to.equal('like')
73 })
74
75 it('Should retrieve ratings list', async function () {
76 await server.videos.rate({ id: videoId, token, rating: 'like' })
77
78 const body = await server.accounts.listRatings({ accountName: 'user', token })
79
80 expect(body.total).to.equal(1)
81 expect(body.data[0].video.id).to.equal(videoId)
82 expect(body.data[0].rating).to.equal('like')
83 })
84
85 it('Should retrieve ratings list by rating type', async function () {
86 {
87 const body = await server.accounts.listRatings({ accountName: 'user', token, rating: 'like' })
88 expect(body.data.length).to.equal(1)
89 }
90
91 {
92 const body = await server.accounts.listRatings({ accountName: 'user', token, rating: 'dislike' })
93 expect(body.data.length).to.equal(0)
94 }
95 })
96 })
97
98 describe('Remove video', function () {
99
100 it('Should not be able to remove the video with an incorrect token', async function () {
101 await server.videos.remove({ token: 'bad_token', id: videoId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
102 })
103
104 it('Should not be able to remove the video with the token of another account', async function () {
105 await server.videos.remove({ token: anotherUserToken, id: videoId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
106 })
107
108 it('Should be able to remove the video with the correct token', async function () {
109 await server.videos.remove({ token, id: videoId })
110 await server.videos.remove({ token, id: videoId2 })
111 })
112 })
113
114 describe('My videos & quotas', function () {
115
116 it('Should be able to upload a video with a user', async function () {
117 this.timeout(30000)
118
119 const attributes = {
120 name: 'super user video',
121 fixture: 'video_short.webm'
122 }
123 await server.videos.upload({ token, attributes })
124
125 await server.channels.create({ token, attributes: { name: 'other_channel' } })
126 })
127
128 it('Should have video quota updated', async function () {
129 const quota = await server.users.getMyQuotaUsed({ token })
130 expect(quota.videoQuotaUsed).to.equal(218910)
131 expect(quota.videoQuotaUsedDaily).to.equal(218910)
132
133 const { data } = await server.users.list()
134 const tmpUser = data.find(u => u.username === 'user')
135 expect(tmpUser.videoQuotaUsed).to.equal(218910)
136 expect(tmpUser.videoQuotaUsedDaily).to.equal(218910)
137 })
138
139 it('Should be able to list my videos', async function () {
140 const { total, data } = await server.videos.listMyVideos({ token })
141 expect(total).to.equal(1)
142 expect(data).to.have.lengthOf(1)
143
144 const video = data[0]
145 expect(video.name).to.equal('super user video')
146 expect(video.thumbnailPath).to.not.be.null
147 expect(video.previewPath).to.not.be.null
148 })
149
150 it('Should be able to filter by channel in my videos', async function () {
151 const myInfo = await server.users.getMyInfo({ token })
152 const mainChannel = myInfo.videoChannels.find(c => c.name !== 'other_channel')
153 const otherChannel = myInfo.videoChannels.find(c => c.name === 'other_channel')
154
155 {
156 const { total, data } = await server.videos.listMyVideos({ token, channelId: mainChannel.id })
157 expect(total).to.equal(1)
158 expect(data).to.have.lengthOf(1)
159
160 const video = data[0]
161 expect(video.name).to.equal('super user video')
162 expect(video.thumbnailPath).to.not.be.null
163 expect(video.previewPath).to.not.be.null
164 }
165
166 {
167 const { total, data } = await server.videos.listMyVideos({ token, channelId: otherChannel.id })
168 expect(total).to.equal(0)
169 expect(data).to.have.lengthOf(0)
170 }
171 })
172
173 it('Should be able to search in my videos', async function () {
174 {
175 const { total, data } = await server.videos.listMyVideos({ token, sort: '-createdAt', search: 'user video' })
176 expect(total).to.equal(1)
177 expect(data).to.have.lengthOf(1)
178 }
179
180 {
181 const { total, data } = await server.videos.listMyVideos({ token, sort: '-createdAt', search: 'toto' })
182 expect(total).to.equal(0)
183 expect(data).to.have.lengthOf(0)
184 }
185 })
186
187 it('Should disable web videos, enable HLS, and update my quota', async function () {
188 this.timeout(160000)
189
190 {
191 const config = await server.config.getCustomConfig()
192 config.transcoding.webVideos.enabled = false
193 config.transcoding.hls.enabled = true
194 config.transcoding.enabled = true
195 await server.config.updateCustomSubConfig({ newConfig: config })
196 }
197
198 {
199 const attributes = {
200 name: 'super user video 2',
201 fixture: 'video_short.webm'
202 }
203 await server.videos.upload({ token, attributes })
204
205 await waitJobs([ server ])
206 }
207
208 {
209 const data = await server.users.getMyQuotaUsed({ token })
210 expect(data.videoQuotaUsed).to.be.greaterThan(220000)
211 expect(data.videoQuotaUsedDaily).to.be.greaterThan(220000)
212 }
213 })
214 })
215
216 after(async function () {
217 await cleanupTests([ server ])
218 })
219})
diff --git a/server/tests/api/users/users-email-verification.ts b/server/tests/api/users/users-email-verification.ts
deleted file mode 100644
index 909226311..000000000
--- a/server/tests/api/users/users-email-verification.ts
+++ /dev/null
@@ -1,165 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { MockSmtpServer } from '@server/tests/shared'
5import { HttpStatusCode } from '@shared/models'
6import {
7 cleanupTests,
8 ConfigCommand,
9 createSingleServer,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 waitJobs
13} from '@shared/server-commands'
14
15describe('Test users email verification', function () {
16 let server: PeerTubeServer
17 let userId: number
18 let userAccessToken: string
19 let verificationString: string
20 let expectedEmailsLength = 0
21 const user1 = {
22 username: 'user_1',
23 password: 'super password'
24 }
25 const user2 = {
26 username: 'user_2',
27 password: 'super password'
28 }
29 const emails: object[] = []
30
31 before(async function () {
32 this.timeout(30000)
33
34 const port = await MockSmtpServer.Instance.collectEmails(emails)
35 server = await createSingleServer(1, ConfigCommand.getEmailOverrideConfig(port))
36
37 await setAccessTokensToServers([ server ])
38 })
39
40 it('Should register user and send verification email if verification required', async function () {
41 this.timeout(30000)
42
43 await server.config.updateExistingSubConfig({
44 newConfig: {
45 signup: {
46 enabled: true,
47 requiresApproval: false,
48 requiresEmailVerification: true,
49 limit: 10
50 }
51 }
52 })
53
54 await server.registrations.register(user1)
55
56 await waitJobs(server)
57 expectedEmailsLength++
58 expect(emails).to.have.lengthOf(expectedEmailsLength)
59
60 const email = emails[expectedEmailsLength - 1]
61
62 const verificationStringMatches = /verificationString=([a-z0-9]+)/.exec(email['text'])
63 expect(verificationStringMatches).not.to.be.null
64
65 verificationString = verificationStringMatches[1]
66 expect(verificationString).to.have.length.above(2)
67
68 const userIdMatches = /userId=([0-9]+)/.exec(email['text'])
69 expect(userIdMatches).not.to.be.null
70
71 userId = parseInt(userIdMatches[1], 10)
72
73 const body = await server.users.get({ userId })
74 expect(body.emailVerified).to.be.false
75 })
76
77 it('Should not allow login for user with unverified email', async function () {
78 const { detail } = await server.login.login({ user: user1, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
79 expect(detail).to.contain('User email is not verified.')
80 })
81
82 it('Should verify the user via email and allow login', async function () {
83 await server.users.verifyEmail({ userId, verificationString })
84
85 const body = await server.login.login({ user: user1 })
86 userAccessToken = body.access_token
87
88 const user = await server.users.get({ userId })
89 expect(user.emailVerified).to.be.true
90 })
91
92 it('Should be able to change the user email', async function () {
93 let updateVerificationString: string
94
95 {
96 await server.users.updateMe({
97 token: userAccessToken,
98 email: 'updated@example.com',
99 currentPassword: user1.password
100 })
101
102 await waitJobs(server)
103 expectedEmailsLength++
104 expect(emails).to.have.lengthOf(expectedEmailsLength)
105
106 const email = emails[expectedEmailsLength - 1]
107
108 const verificationStringMatches = /verificationString=([a-z0-9]+)/.exec(email['text'])
109 updateVerificationString = verificationStringMatches[1]
110 }
111
112 {
113 const me = await server.users.getMyInfo({ token: userAccessToken })
114 expect(me.email).to.equal('user_1@example.com')
115 expect(me.pendingEmail).to.equal('updated@example.com')
116 }
117
118 {
119 await server.users.verifyEmail({ userId, verificationString: updateVerificationString, isPendingEmail: true })
120
121 const me = await server.users.getMyInfo({ token: userAccessToken })
122 expect(me.email).to.equal('updated@example.com')
123 expect(me.pendingEmail).to.be.null
124 }
125 })
126
127 it('Should register user not requiring email verification if setting not enabled', async function () {
128 this.timeout(5000)
129 await server.config.updateExistingSubConfig({
130 newConfig: {
131 signup: {
132 requiresEmailVerification: false
133 }
134 }
135 })
136
137 await server.registrations.register(user2)
138
139 await waitJobs(server)
140 expect(emails).to.have.lengthOf(expectedEmailsLength)
141
142 const accessToken = await server.login.getAccessToken(user2)
143
144 const user = await server.users.getMyInfo({ token: accessToken })
145 expect(user.emailVerified).to.be.null
146 })
147
148 it('Should allow login for user with unverified email when setting later enabled', async function () {
149 await server.config.updateCustomSubConfig({
150 newConfig: {
151 signup: {
152 requiresEmailVerification: true
153 }
154 }
155 })
156
157 await server.login.getAccessToken(user2)
158 })
159
160 after(async function () {
161 MockSmtpServer.Instance.kill()
162
163 await cleanupTests([ server ])
164 })
165})
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
deleted file mode 100644
index 3823b74ef..000000000
--- a/server/tests/api/users/users-multiple-servers.ts
+++ /dev/null
@@ -1,216 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import {
5 checkActorFilesWereRemoved,
6 checkTmpIsEmpty,
7 checkVideoFilesWereRemoved,
8 saveVideoInServers,
9 testImage
10} from '@server/tests/shared'
11import { MyUser } from '@shared/models'
12import {
13 cleanupTests,
14 createMultipleServers,
15 doubleFollow,
16 PeerTubeServer,
17 setAccessTokensToServers,
18 setDefaultChannelAvatar,
19 waitJobs
20} from '@shared/server-commands'
21
22describe('Test users with multiple servers', function () {
23 let servers: PeerTubeServer[] = []
24
25 let user: MyUser
26 let userId: number
27
28 let videoUUID: string
29 let userAccessToken: string
30 let userAvatarFilenames: string[]
31
32 before(async function () {
33 this.timeout(120_000)
34
35 servers = await createMultipleServers(3)
36
37 // Get the access tokens
38 await setAccessTokensToServers(servers)
39 await setDefaultChannelAvatar(servers)
40
41 // Server 1 and server 2 follow each other
42 await doubleFollow(servers[0], servers[1])
43 // Server 1 and server 3 follow each other
44 await doubleFollow(servers[0], servers[2])
45 // Server 2 and server 3 follow each other
46 await doubleFollow(servers[1], servers[2])
47
48 // The root user of server 1 is propagated to servers 2 and 3
49 await servers[0].videos.upload()
50
51 {
52 const username = 'user1'
53 const created = await servers[0].users.create({ username })
54 userId = created.id
55 userAccessToken = await servers[0].login.getAccessToken(username)
56 }
57
58 {
59 const { uuid } = await servers[0].videos.upload({ token: userAccessToken })
60 videoUUID = uuid
61
62 await waitJobs(servers)
63
64 await saveVideoInServers(servers, videoUUID)
65 }
66 })
67
68 it('Should be able to update my display name', async function () {
69 await servers[0].users.updateMe({ displayName: 'my super display name' })
70
71 user = await servers[0].users.getMyInfo()
72 expect(user.account.displayName).to.equal('my super display name')
73
74 await waitJobs(servers)
75 })
76
77 it('Should be able to update my description', async function () {
78 this.timeout(10_000)
79
80 await servers[0].users.updateMe({ description: 'my super description updated' })
81
82 user = await servers[0].users.getMyInfo()
83 expect(user.account.displayName).to.equal('my super display name')
84 expect(user.account.description).to.equal('my super description updated')
85
86 await waitJobs(servers)
87 })
88
89 it('Should be able to update my avatar', async function () {
90 this.timeout(10_000)
91
92 const fixture = 'avatar2.png'
93
94 await servers[0].users.updateMyAvatar({ fixture })
95
96 user = await servers[0].users.getMyInfo()
97 userAvatarFilenames = user.account.avatars.map(({ path }) => path)
98
99 for (const avatar of user.account.avatars) {
100 await testImage(servers[0].url, `avatar2-resized-${avatar.width}x${avatar.width}`, avatar.path, '.png')
101 }
102
103 await waitJobs(servers)
104 })
105
106 it('Should have updated my profile on other servers too', async function () {
107 let createdAt: string | Date
108
109 for (const server of servers) {
110 const body = await server.accounts.list({ sort: '-createdAt' })
111
112 const resList = body.data.find(a => a.name === 'root' && a.host === servers[0].host)
113 expect(resList).not.to.be.undefined
114
115 const account = await server.accounts.get({ accountName: resList.name + '@' + resList.host })
116
117 if (!createdAt) createdAt = account.createdAt
118
119 expect(account.name).to.equal('root')
120 expect(account.host).to.equal(servers[0].host)
121 expect(account.displayName).to.equal('my super display name')
122 expect(account.description).to.equal('my super description updated')
123 expect(createdAt).to.equal(account.createdAt)
124
125 if (server.serverNumber === 1) {
126 expect(account.userId).to.be.a('number')
127 } else {
128 expect(account.userId).to.be.undefined
129 }
130
131 for (const avatar of account.avatars) {
132 await testImage(server.url, `avatar2-resized-${avatar.width}x${avatar.width}`, avatar.path, '.png')
133 }
134 }
135 })
136
137 it('Should list account videos', async function () {
138 for (const server of servers) {
139 const { total, data } = await server.videos.listByAccount({ handle: 'user1@' + servers[0].host })
140
141 expect(total).to.equal(1)
142 expect(data).to.be.an('array')
143 expect(data).to.have.lengthOf(1)
144 expect(data[0].uuid).to.equal(videoUUID)
145 }
146 })
147
148 it('Should search through account videos', async function () {
149 const created = await servers[0].videos.upload({ token: userAccessToken, attributes: { name: 'Kami no chikara' } })
150
151 await waitJobs(servers)
152
153 for (const server of servers) {
154 const { total, data } = await server.videos.listByAccount({ handle: 'user1@' + servers[0].host, search: 'Kami' })
155
156 expect(total).to.equal(1)
157 expect(data).to.be.an('array')
158 expect(data).to.have.lengthOf(1)
159 expect(data[0].uuid).to.equal(created.uuid)
160 }
161 })
162
163 it('Should remove the user', async function () {
164 this.timeout(10_000)
165
166 for (const server of servers) {
167 const body = await server.accounts.list({ sort: '-createdAt' })
168
169 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === servers[0].host)
170 expect(accountDeleted).not.to.be.undefined
171
172 const { data } = await server.channels.list()
173 const videoChannelDeleted = data.find(a => a.displayName === 'Main user1 channel' && a.host === servers[0].host)
174 expect(videoChannelDeleted).not.to.be.undefined
175 }
176
177 await servers[0].users.remove({ userId })
178
179 await waitJobs(servers)
180
181 for (const server of servers) {
182 const body = await server.accounts.list({ sort: '-createdAt' })
183
184 const accountDeleted = body.data.find(a => a.name === 'user1' && a.host === servers[0].host)
185 expect(accountDeleted).to.be.undefined
186
187 const { data } = await server.channels.list()
188 const videoChannelDeleted = data.find(a => a.name === 'Main user1 channel' && a.host === servers[0].host)
189 expect(videoChannelDeleted).to.be.undefined
190 }
191 })
192
193 it('Should not have actor files', async () => {
194 for (const server of servers) {
195 for (const userAvatarFilename of userAvatarFilenames) {
196 await checkActorFilesWereRemoved(userAvatarFilename, server)
197 }
198 }
199 })
200
201 it('Should not have video files', async () => {
202 for (const server of servers) {
203 await checkVideoFilesWereRemoved({ server, video: server.store.videoDetails })
204 }
205 })
206
207 it('Should have an empty tmp directory', async function () {
208 for (const server of servers) {
209 await checkTmpIsEmpty(server)
210 }
211 })
212
213 after(async function () {
214 await cleanupTests(servers)
215 })
216})
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
deleted file mode 100644
index 67ade1d0d..000000000
--- a/server/tests/api/users/users.ts
+++ /dev/null
@@ -1,529 +0,0 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { testImageSize } from '@server/tests/shared'
5import { AbuseState, HttpStatusCode, UserAdminFlag, UserRole, VideoPlaylistType } from '@shared/models'
6import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
7
8describe('Test users', function () {
9 let server: PeerTubeServer
10 let token: string
11 let userToken: string
12 let videoId: number
13 let userId: number
14 const user = {
15 username: 'user_1',
16 password: 'super password'
17 }
18
19 before(async function () {
20 this.timeout(30000)
21
22 server = await createSingleServer(1, {
23 rates_limit: {
24 login: {
25 max: 30
26 }
27 }
28 })
29
30 await setAccessTokensToServers([ server ])
31
32 await server.plugins.install({ npmName: 'peertube-theme-background-red' })
33 })
34
35 describe('Creating a user', function () {
36
37 it('Should be able to create a new user', async function () {
38 await server.users.create({ ...user, videoQuota: 2 * 1024 * 1024, adminFlags: UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST })
39 })
40
41 it('Should be able to login with this user', async function () {
42 userToken = await server.login.getAccessToken(user)
43 })
44
45 it('Should be able to get user information', async function () {
46 const userMe = await server.users.getMyInfo({ token: userToken })
47
48 const userGet = await server.users.get({ userId: userMe.id, withStats: true })
49
50 for (const user of [ userMe, userGet ]) {
51 expect(user.username).to.equal('user_1')
52 expect(user.email).to.equal('user_1@example.com')
53 expect(user.nsfwPolicy).to.equal('display')
54 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
55 expect(user.role.label).to.equal('User')
56 expect(user.id).to.be.a('number')
57 expect(user.account.displayName).to.equal('user_1')
58 expect(user.account.description).to.be.null
59 }
60
61 expect(userMe.adminFlags).to.equal(UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST)
62 expect(userGet.adminFlags).to.equal(UserAdminFlag.BYPASS_VIDEO_AUTO_BLACKLIST)
63
64 expect(userMe.specialPlaylists).to.have.lengthOf(1)
65 expect(userMe.specialPlaylists[0].type).to.equal(VideoPlaylistType.WATCH_LATER)
66
67 // Check stats are included with withStats
68 expect(userGet.videosCount).to.be.a('number')
69 expect(userGet.videosCount).to.equal(0)
70 expect(userGet.videoCommentsCount).to.be.a('number')
71 expect(userGet.videoCommentsCount).to.equal(0)
72 expect(userGet.abusesCount).to.be.a('number')
73 expect(userGet.abusesCount).to.equal(0)
74 expect(userGet.abusesAcceptedCount).to.be.a('number')
75 expect(userGet.abusesAcceptedCount).to.equal(0)
76 })
77 })
78
79 describe('Users listing', function () {
80
81 it('Should list all the users', async function () {
82 const { data, total } = await server.users.list()
83
84 expect(total).to.equal(2)
85 expect(data).to.be.an('array')
86 expect(data.length).to.equal(2)
87
88 const user = data[0]
89 expect(user.username).to.equal('user_1')
90 expect(user.email).to.equal('user_1@example.com')
91 expect(user.nsfwPolicy).to.equal('display')
92
93 const rootUser = data[1]
94 expect(rootUser.username).to.equal('root')
95 expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com')
96 expect(user.nsfwPolicy).to.equal('display')
97
98 expect(rootUser.lastLoginDate).to.exist
99 expect(user.lastLoginDate).to.exist
100
101 userId = user.id
102 })
103
104 it('Should list only the first user by username asc', async function () {
105 const { total, data } = await server.users.list({ start: 0, count: 1, sort: 'username' })
106
107 expect(total).to.equal(2)
108 expect(data.length).to.equal(1)
109
110 const user = data[0]
111 expect(user.username).to.equal('root')
112 expect(user.email).to.equal('admin' + server.internalServerNumber + '@example.com')
113 expect(user.role.label).to.equal('Administrator')
114 expect(user.nsfwPolicy).to.equal('display')
115 })
116
117 it('Should list only the first user by username desc', async function () {
118 const { total, data } = await server.users.list({ start: 0, count: 1, sort: '-username' })
119
120 expect(total).to.equal(2)
121 expect(data.length).to.equal(1)
122
123 const user = data[0]
124 expect(user.username).to.equal('user_1')
125 expect(user.email).to.equal('user_1@example.com')
126 expect(user.nsfwPolicy).to.equal('display')
127 })
128
129 it('Should list only the second user by createdAt desc', async function () {
130 const { data, total } = await server.users.list({ start: 0, count: 1, sort: '-createdAt' })
131 expect(total).to.equal(2)
132
133 expect(data.length).to.equal(1)
134
135 const user = data[0]
136 expect(user.username).to.equal('user_1')
137 expect(user.email).to.equal('user_1@example.com')
138 expect(user.nsfwPolicy).to.equal('display')
139 })
140
141 it('Should list all the users by createdAt asc', async function () {
142 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt' })
143
144 expect(total).to.equal(2)
145 expect(data.length).to.equal(2)
146
147 expect(data[0].username).to.equal('root')
148 expect(data[0].email).to.equal('admin' + server.internalServerNumber + '@example.com')
149 expect(data[0].nsfwPolicy).to.equal('display')
150
151 expect(data[1].username).to.equal('user_1')
152 expect(data[1].email).to.equal('user_1@example.com')
153 expect(data[1].nsfwPolicy).to.equal('display')
154 })
155
156 it('Should search user by username', async function () {
157 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'oot' })
158 expect(total).to.equal(1)
159 expect(data.length).to.equal(1)
160 expect(data[0].username).to.equal('root')
161 })
162
163 it('Should search user by email', async function () {
164 {
165 const { total, data } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'r_1@exam' })
166 expect(total).to.equal(1)
167 expect(data.length).to.equal(1)
168 expect(data[0].username).to.equal('user_1')
169 expect(data[0].email).to.equal('user_1@example.com')
170 }
171
172 {
173 const { total, data } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', search: 'example' })
174 expect(total).to.equal(2)
175 expect(data.length).to.equal(2)
176 expect(data[0].username).to.equal('root')
177 expect(data[1].username).to.equal('user_1')
178 }
179 })
180 })
181
182 describe('Update my account', function () {
183
184 it('Should update my password', async function () {
185 await server.users.updateMe({
186 token: userToken,
187 currentPassword: 'super password',
188 password: 'new password'
189 })
190 user.password = 'new password'
191
192 await server.login.login({ user })
193 })
194
195 it('Should be able to change the NSFW display attribute', async function () {
196 await server.users.updateMe({
197 token: userToken,
198 nsfwPolicy: 'do_not_list'
199 })
200
201 const user = await server.users.getMyInfo({ token: userToken })
202 expect(user.username).to.equal('user_1')
203 expect(user.email).to.equal('user_1@example.com')
204 expect(user.nsfwPolicy).to.equal('do_not_list')
205 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
206 expect(user.id).to.be.a('number')
207 expect(user.account.displayName).to.equal('user_1')
208 expect(user.account.description).to.be.null
209 })
210
211 it('Should be able to change the autoPlayVideo attribute', async function () {
212 await server.users.updateMe({
213 token: userToken,
214 autoPlayVideo: false
215 })
216
217 const user = await server.users.getMyInfo({ token: userToken })
218 expect(user.autoPlayVideo).to.be.false
219 })
220
221 it('Should be able to change the autoPlayNextVideo attribute', async function () {
222 await server.users.updateMe({
223 token: userToken,
224 autoPlayNextVideo: true
225 })
226
227 const user = await server.users.getMyInfo({ token: userToken })
228 expect(user.autoPlayNextVideo).to.be.true
229 })
230
231 it('Should be able to change the p2p attribute', async function () {
232 await server.users.updateMe({
233 token: userToken,
234 p2pEnabled: true
235 })
236
237 const user = await server.users.getMyInfo({ token: userToken })
238 expect(user.p2pEnabled).to.be.true
239 })
240
241 it('Should be able to change the email attribute', async function () {
242 await server.users.updateMe({
243 token: userToken,
244 currentPassword: 'new password',
245 email: 'updated@example.com'
246 })
247
248 const user = await server.users.getMyInfo({ token: userToken })
249 expect(user.username).to.equal('user_1')
250 expect(user.email).to.equal('updated@example.com')
251 expect(user.nsfwPolicy).to.equal('do_not_list')
252 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
253 expect(user.id).to.be.a('number')
254 expect(user.account.displayName).to.equal('user_1')
255 expect(user.account.description).to.be.null
256 })
257
258 it('Should be able to update my avatar with a gif', async function () {
259 const fixture = 'avatar.gif'
260
261 await server.users.updateMyAvatar({ token: userToken, fixture })
262
263 const user = await server.users.getMyInfo({ token: userToken })
264 for (const avatar of user.account.avatars) {
265 await testImageSize(server.url, `avatar-resized-${avatar.width}x${avatar.width}`, avatar.path, '.gif')
266 }
267 })
268
269 it('Should be able to update my avatar with a gif, and then a png', async function () {
270 for (const extension of [ '.png', '.gif' ]) {
271 const fixture = 'avatar' + extension
272
273 await server.users.updateMyAvatar({ token: userToken, fixture })
274
275 const user = await server.users.getMyInfo({ token: userToken })
276 for (const avatar of user.account.avatars) {
277 await testImageSize(server.url, `avatar-resized-${avatar.width}x${avatar.width}`, avatar.path, extension)
278 }
279 }
280 })
281
282 it('Should be able to update my display name', async function () {
283 await server.users.updateMe({ token: userToken, displayName: 'new display name' })
284
285 const user = await server.users.getMyInfo({ token: userToken })
286 expect(user.username).to.equal('user_1')
287 expect(user.email).to.equal('updated@example.com')
288 expect(user.nsfwPolicy).to.equal('do_not_list')
289 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
290 expect(user.id).to.be.a('number')
291 expect(user.account.displayName).to.equal('new display name')
292 expect(user.account.description).to.be.null
293 })
294
295 it('Should be able to update my description', async function () {
296 await server.users.updateMe({ token: userToken, description: 'my super description updated' })
297
298 const user = await server.users.getMyInfo({ token: userToken })
299 expect(user.username).to.equal('user_1')
300 expect(user.email).to.equal('updated@example.com')
301 expect(user.nsfwPolicy).to.equal('do_not_list')
302 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
303 expect(user.id).to.be.a('number')
304 expect(user.account.displayName).to.equal('new display name')
305 expect(user.account.description).to.equal('my super description updated')
306 expect(user.noWelcomeModal).to.be.false
307 expect(user.noInstanceConfigWarningModal).to.be.false
308 expect(user.noAccountSetupWarningModal).to.be.false
309 })
310
311 it('Should be able to update my theme', async function () {
312 for (const theme of [ 'background-red', 'default', 'instance-default' ]) {
313 await server.users.updateMe({ token: userToken, theme })
314
315 const user = await server.users.getMyInfo({ token: userToken })
316 expect(user.theme).to.equal(theme)
317 }
318 })
319
320 it('Should be able to update my modal preferences', async function () {
321 await server.users.updateMe({
322 token: userToken,
323 noInstanceConfigWarningModal: true,
324 noWelcomeModal: true,
325 noAccountSetupWarningModal: true
326 })
327
328 const user = await server.users.getMyInfo({ token: userToken })
329 expect(user.noWelcomeModal).to.be.true
330 expect(user.noInstanceConfigWarningModal).to.be.true
331 expect(user.noAccountSetupWarningModal).to.be.true
332 })
333 })
334
335 describe('Updating another user', function () {
336
337 it('Should be able to update another user', async function () {
338 await server.users.update({
339 userId,
340 token,
341 email: 'updated2@example.com',
342 emailVerified: true,
343 videoQuota: 42,
344 role: UserRole.MODERATOR,
345 adminFlags: UserAdminFlag.NONE,
346 pluginAuth: 'toto'
347 })
348
349 const user = await server.users.get({ token, userId })
350
351 expect(user.username).to.equal('user_1')
352 expect(user.email).to.equal('updated2@example.com')
353 expect(user.emailVerified).to.be.true
354 expect(user.nsfwPolicy).to.equal('do_not_list')
355 expect(user.videoQuota).to.equal(42)
356 expect(user.role.label).to.equal('Moderator')
357 expect(user.id).to.be.a('number')
358 expect(user.adminFlags).to.equal(UserAdminFlag.NONE)
359 expect(user.pluginAuth).to.equal('toto')
360 })
361
362 it('Should reset the auth plugin', async function () {
363 await server.users.update({ userId, token, pluginAuth: null })
364
365 const user = await server.users.get({ token, userId })
366 expect(user.pluginAuth).to.be.null
367 })
368
369 it('Should have removed the user token', async function () {
370 await server.users.getMyQuotaUsed({ token: userToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
371
372 userToken = await server.login.getAccessToken(user)
373 })
374
375 it('Should be able to update another user password', async function () {
376 await server.users.update({ userId, token, password: 'password updated' })
377
378 await server.users.getMyQuotaUsed({ token: userToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
379
380 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
381
382 user.password = 'password updated'
383 userToken = await server.login.getAccessToken(user)
384 })
385 })
386
387 describe('Remove a user', function () {
388
389 before(async function () {
390 await server.users.update({
391 userId,
392 token,
393 videoQuota: 2 * 1024 * 1024
394 })
395
396 await server.videos.quickUpload({ name: 'user video', token: userToken, fixture: 'video_short.webm' })
397 await server.videos.quickUpload({ name: 'root video' })
398
399 const { total } = await server.videos.list()
400 expect(total).to.equal(2)
401 })
402
403 it('Should be able to remove this user', async function () {
404 await server.users.remove({ userId, token })
405 })
406
407 it('Should not be able to login with this user', async function () {
408 await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
409 })
410
411 it('Should not have videos of this user', async function () {
412 const { data, total } = await server.videos.list()
413 expect(total).to.equal(1)
414
415 const video = data[0]
416 expect(video.account.name).to.equal('root')
417 })
418 })
419
420 describe('User blocking', function () {
421 let user16Id: number
422 let user16AccessToken: string
423
424 const user16 = {
425 username: 'user_16',
426 password: 'my super password'
427 }
428
429 it('Should block a user', async function () {
430 const user = await server.users.create({ ...user16 })
431 user16Id = user.id
432
433 user16AccessToken = await server.login.getAccessToken(user16)
434
435 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.OK_200 })
436 await server.users.banUser({ userId: user16Id })
437
438 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
439 await server.login.login({ user: user16, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
440 })
441
442 it('Should search user by banned status', async function () {
443 {
444 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', blocked: true })
445 expect(total).to.equal(1)
446 expect(data.length).to.equal(1)
447
448 expect(data[0].username).to.equal(user16.username)
449 }
450
451 {
452 const { data, total } = await server.users.list({ start: 0, count: 2, sort: 'createdAt', blocked: false })
453 expect(total).to.equal(1)
454 expect(data.length).to.equal(1)
455
456 expect(data[0].username).to.not.equal(user16.username)
457 }
458 })
459
460 it('Should unblock a user', async function () {
461 await server.users.unbanUser({ userId: user16Id })
462 user16AccessToken = await server.login.getAccessToken(user16)
463 await server.users.getMyInfo({ token: user16AccessToken, expectedStatus: HttpStatusCode.OK_200 })
464 })
465 })
466
467 describe('User stats', function () {
468 let user17Id: number
469 let user17AccessToken: string
470
471 it('Should report correct initial statistics about a user', async function () {
472 const user17 = {
473 username: 'user_17',
474 password: 'my super password'
475 }
476 const created = await server.users.create({ ...user17 })
477
478 user17Id = created.id
479 user17AccessToken = await server.login.getAccessToken(user17)
480
481 const user = await server.users.get({ userId: user17Id, withStats: true })
482 expect(user.videosCount).to.equal(0)
483 expect(user.videoCommentsCount).to.equal(0)
484 expect(user.abusesCount).to.equal(0)
485 expect(user.abusesCreatedCount).to.equal(0)
486 expect(user.abusesAcceptedCount).to.equal(0)
487 })
488
489 it('Should report correct videos count', async function () {
490 const attributes = { name: 'video to test user stats' }
491 await server.videos.upload({ token: user17AccessToken, attributes })
492
493 const { data } = await server.videos.list()
494 videoId = data.find(video => video.name === attributes.name).id
495
496 const user = await server.users.get({ userId: user17Id, withStats: true })
497 expect(user.videosCount).to.equal(1)
498 })
499
500 it('Should report correct video comments for user', async function () {
501 const text = 'super comment'
502 await server.comments.createThread({ token: user17AccessToken, videoId, text })
503
504 const user = await server.users.get({ userId: user17Id, withStats: true })
505 expect(user.videoCommentsCount).to.equal(1)
506 })
507
508 it('Should report correct abuses counts', async function () {
509 const reason = 'my super bad reason'
510 await server.abuses.report({ token: user17AccessToken, videoId, reason })
511
512 const body1 = await server.abuses.getAdminList()
513 const abuseId = body1.data[0].id
514
515 const user2 = await server.users.get({ userId: user17Id, withStats: true })
516 expect(user2.abusesCount).to.equal(1) // number of incriminations
517 expect(user2.abusesCreatedCount).to.equal(1) // number of reports created
518
519 await server.abuses.update({ abuseId, body: { state: AbuseState.ACCEPTED } })
520
521 const user3 = await server.users.get({ userId: user17Id, withStats: true })
522 expect(user3.abusesAcceptedCount).to.equal(1) // number of reports created accepted
523 })
524 })
525
526 after(async function () {
527 await cleanupTests([ server ])
528 })
529})