diff options
author | Chocobozzz <me@florianbigard.com> | 2023-07-31 14:34:36 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-08-11 15:02:33 +0200 |
commit | 3a4992633ee62d5edfbb484d9c6bcb3cf158489d (patch) | |
tree | e4510b39bdac9c318fdb4b47018d08f15368b8f0 /server/tests/api/users | |
parent | 04d1da5621d25d59bd5fa1543b725c497bf5d9a8 (diff) | |
download | PeerTube-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.ts | 8 | ||||
-rw-r--r-- | server/tests/api/users/oauth.ts | 197 | ||||
-rw-r--r-- | server/tests/api/users/registrations.ts | 415 | ||||
-rw-r--r-- | server/tests/api/users/two-factor.ts | 200 | ||||
-rw-r--r-- | server/tests/api/users/user-subscriptions.ts | 614 | ||||
-rw-r--r-- | server/tests/api/users/user-videos.ts | 219 | ||||
-rw-r--r-- | server/tests/api/users/users-email-verification.ts | 165 | ||||
-rw-r--r-- | server/tests/api/users/users-multiple-servers.ts | 216 | ||||
-rw-r--r-- | server/tests/api/users/users.ts | 529 |
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 @@ | |||
1 | import './oauth' | ||
2 | import './registrations`' | ||
3 | import './two-factor' | ||
4 | import './user-subscriptions' | ||
5 | import './user-videos' | ||
6 | import './users' | ||
7 | import './users-multiple-servers' | ||
8 | import './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 | |||
3 | import { expect } from 'chai' | ||
4 | import { SQLCommand } from '@server/tests/shared' | ||
5 | import { wait } from '@shared/core-utils' | ||
6 | import { HttpStatusCode, OAuth2ErrorCode, PeerTubeProblemDocument } from '@shared/models' | ||
7 | import { cleanupTests, createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands' | ||
8 | |||
9 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { MockSmtpServer } from '@server/tests/shared' | ||
5 | import { UserRegistrationState, UserRole } from '@shared/models' | ||
6 | import { | ||
7 | cleanupTests, | ||
8 | ConfigCommand, | ||
9 | createSingleServer, | ||
10 | PeerTubeServer, | ||
11 | setAccessTokensToServers, | ||
12 | waitJobs | ||
13 | } from '@shared/server-commands' | ||
14 | |||
15 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { expectStartWith } from '@server/tests/shared' | ||
5 | import { HttpStatusCode } from '@shared/models' | ||
6 | import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers, TwoFactorCommand } from '@shared/server-commands' | ||
7 | |||
8 | async 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 | |||
23 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { VideoPrivacy } from '@shared/models' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | createMultipleServers, | ||
8 | doubleFollow, | ||
9 | PeerTubeServer, | ||
10 | setAccessTokensToServers, | ||
11 | setDefaultAccountAvatar, | ||
12 | setDefaultChannelAvatar, | ||
13 | SubscriptionsCommand, | ||
14 | waitJobs | ||
15 | } from '@shared/server-commands' | ||
16 | |||
17 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { HttpStatusCode } from '@shared/models' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | createSingleServer, | ||
8 | PeerTubeServer, | ||
9 | setAccessTokensToServers, | ||
10 | setDefaultAccountAvatar, | ||
11 | setDefaultChannelAvatar, | ||
12 | waitJobs | ||
13 | } from '@shared/server-commands' | ||
14 | |||
15 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { MockSmtpServer } from '@server/tests/shared' | ||
5 | import { HttpStatusCode } from '@shared/models' | ||
6 | import { | ||
7 | cleanupTests, | ||
8 | ConfigCommand, | ||
9 | createSingleServer, | ||
10 | PeerTubeServer, | ||
11 | setAccessTokensToServers, | ||
12 | waitJobs | ||
13 | } from '@shared/server-commands' | ||
14 | |||
15 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { | ||
5 | checkActorFilesWereRemoved, | ||
6 | checkTmpIsEmpty, | ||
7 | checkVideoFilesWereRemoved, | ||
8 | saveVideoInServers, | ||
9 | testImage | ||
10 | } from '@server/tests/shared' | ||
11 | import { MyUser } from '@shared/models' | ||
12 | import { | ||
13 | cleanupTests, | ||
14 | createMultipleServers, | ||
15 | doubleFollow, | ||
16 | PeerTubeServer, | ||
17 | setAccessTokensToServers, | ||
18 | setDefaultChannelAvatar, | ||
19 | waitJobs | ||
20 | } from '@shared/server-commands' | ||
21 | |||
22 | describe('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 | |||
3 | import { expect } from 'chai' | ||
4 | import { testImageSize } from '@server/tests/shared' | ||
5 | import { AbuseState, HttpStatusCode, UserAdminFlag, UserRole, VideoPlaylistType } from '@shared/models' | ||
6 | import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands' | ||
7 | |||
8 | describe('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 | }) | ||