diff options
author | Florent <florent.git@zeteo.me> | 2022-08-10 09:53:39 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-10 09:53:39 +0200 |
commit | 2a491182e483b97afb1b65c908b23cb48d591807 (patch) | |
tree | ec13503216ad72a3ea8f1ce3b659899f8167fb47 /server/tests/api/check-params/video-channel-syncs.ts | |
parent | 06ac128958c489efe1008eeca1df683819bd2f18 (diff) | |
download | PeerTube-2a491182e483b97afb1b65c908b23cb48d591807.tar.gz PeerTube-2a491182e483b97afb1b65c908b23cb48d591807.tar.zst PeerTube-2a491182e483b97afb1b65c908b23cb48d591807.zip |
Channel sync (#5135)
* Add external channel URL for channel update / creation (#754)
* Disallow synchronisation if user has no video quota (#754)
* More constraints serverside (#754)
* Disable sync if server configuration does not allow HTTP import (#754)
* Working version synchronizing videos with a job (#754)
TODO: refactoring, too much code duplication
* More logs and try/catch (#754)
* Fix eslint error (#754)
* WIP: support synchronization time change (#754)
* New frontend #754
* WIP: Create sync front (#754)
* Enhance UI, sync creation form (#754)
* Warning message when HTTP upload is disallowed
* More consistent names (#754)
* Binding Front with API (#754)
* Add a /me API (#754)
* Improve list UI (#754)
* Implement creation and deletion routes (#754)
* Lint (#754)
* Lint again (#754)
* WIP: UI for triggering import existing videos (#754)
* Implement jobs for syncing and importing channels
* Don't sync videos before sync creation + avoid concurrency issue (#754)
* Cleanup (#754)
* Cleanup: OpenAPI + API rework (#754)
* Remove dead code (#754)
* Eslint (#754)
* Revert the mess with whitespaces in constants.ts (#754)
* Some fixes after rebase (#754)
* Several fixes after PR remarks (#754)
* Front + API: Rename video-channels-sync to video-channel-syncs (#754)
* Allow enabling channel sync through UI (#754)
* getChannelInfo (#754)
* Minor fixes: openapi + model + sql (#754)
* Simplified API validators (#754)
* Rename MChannelSync to MChannelSyncChannel (#754)
* Add command for VideoChannelSync (#754)
* Use synchronization.enabled config (#754)
* Check parameters test + some fixes (#754)
* Fix conflict mistake (#754)
* Restrict access to video channel sync list API (#754)
* Start adding unit test for synchronization (#754)
* Continue testing (#754)
* Tests finished + convertion of job to scheduler (#754)
* Add lastSyncAt field (#754)
* Fix externalRemoteUrl sort + creation date not well formatted (#754)
* Small fix (#754)
* Factorize addYoutubeDLImport and buildVideo (#754)
* Check duplicates on channel not on users (#754)
* factorize thumbnail generation (#754)
* Fetch error should return status 400 (#754)
* Separate video-channel-import and video-channel-sync-latest (#754)
* Bump DB migration version after rebase (#754)
* Prettier states in UI table (#754)
* Add DefaultScope in VideoChannelSyncModel (#754)
* Fix audit logs (#754)
* Ensure user can upload when importing channel + minor fixes (#754)
* Mark synchronization as failed on exception + typos (#754)
* Change REST API for importing videos into channel (#754)
* Add option for fully synchronize a chnanel (#754)
* Return a whole sync object on creation to avoid tricks in Front (#754)
* Various remarks (#754)
* Single quotes by default (#754)
* Rename synchronization to video_channel_synchronization
* Add check.latest_videos_count and max_per_user options (#754)
* Better channel rendering in list #754
* Allow sorting with channel name and state (#754)
* Add missing tests for channel imports (#754)
* Prefer using a parent job for channel sync
* Styling
* Client styling
Co-authored-by: Chocobozzz <me@florianbigard.com>
Diffstat (limited to 'server/tests/api/check-params/video-channel-syncs.ts')
-rw-r--r-- | server/tests/api/check-params/video-channel-syncs.ts | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/server/tests/api/check-params/video-channel-syncs.ts b/server/tests/api/check-params/video-channel-syncs.ts new file mode 100644 index 000000000..bcd8984df --- /dev/null +++ b/server/tests/api/check-params/video-channel-syncs.ts | |||
@@ -0,0 +1,318 @@ | |||
1 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination, FIXTURE_URLS } from '@server/tests/shared' | ||
2 | import { HttpStatusCode, VideoChannelSyncCreate } from '@shared/models' | ||
3 | import { | ||
4 | ChannelSyncsCommand, | ||
5 | createSingleServer, | ||
6 | makePostBodyRequest, | ||
7 | PeerTubeServer, | ||
8 | setAccessTokensToServers, | ||
9 | setDefaultVideoChannel | ||
10 | } from '@shared/server-commands' | ||
11 | |||
12 | describe('Test video channel sync API validator', () => { | ||
13 | const path = '/api/v1/video-channel-syncs' | ||
14 | let server: PeerTubeServer | ||
15 | let command: ChannelSyncsCommand | ||
16 | let rootChannelId: number | ||
17 | let rootChannelSyncId: number | ||
18 | const userInfo = { | ||
19 | accessToken: '', | ||
20 | username: 'user1', | ||
21 | id: -1, | ||
22 | channelId: -1, | ||
23 | syncId: -1 | ||
24 | } | ||
25 | |||
26 | async function withChannelSyncDisabled<T> (callback: () => Promise<T>): Promise<void> { | ||
27 | try { | ||
28 | await server.config.disableChannelSync() | ||
29 | await callback() | ||
30 | } finally { | ||
31 | await server.config.enableChannelSync() | ||
32 | } | ||
33 | } | ||
34 | |||
35 | async function withMaxSyncsPerUser<T> (maxSync: number, callback: () => Promise<T>): Promise<void> { | ||
36 | const origConfig = await server.config.getCustomConfig() | ||
37 | |||
38 | await server.config.updateExistingSubConfig({ | ||
39 | newConfig: { | ||
40 | import: { | ||
41 | videoChannelSynchronization: { | ||
42 | maxPerUser: maxSync | ||
43 | } | ||
44 | } | ||
45 | } | ||
46 | }) | ||
47 | |||
48 | try { | ||
49 | await callback() | ||
50 | } finally { | ||
51 | await server.config.updateCustomConfig({ newCustomConfig: origConfig }) | ||
52 | } | ||
53 | } | ||
54 | |||
55 | before(async function () { | ||
56 | this.timeout(30_000) | ||
57 | |||
58 | server = await createSingleServer(1) | ||
59 | |||
60 | await setAccessTokensToServers([ server ]) | ||
61 | await setDefaultVideoChannel([ server ]) | ||
62 | |||
63 | command = server.channelSyncs | ||
64 | |||
65 | rootChannelId = server.store.channel.id | ||
66 | |||
67 | { | ||
68 | userInfo.accessToken = await server.users.generateUserAndToken(userInfo.username) | ||
69 | |||
70 | const { videoChannels, id: userId } = await server.users.getMyInfo({ token: userInfo.accessToken }) | ||
71 | userInfo.id = userId | ||
72 | userInfo.channelId = videoChannels[0].id | ||
73 | } | ||
74 | |||
75 | await server.config.enableChannelSync() | ||
76 | }) | ||
77 | |||
78 | describe('When creating a sync', function () { | ||
79 | let baseCorrectParams: VideoChannelSyncCreate | ||
80 | |||
81 | before(function () { | ||
82 | baseCorrectParams = { | ||
83 | externalChannelUrl: FIXTURE_URLS.youtubeChannel, | ||
84 | videoChannelId: rootChannelId | ||
85 | } | ||
86 | }) | ||
87 | |||
88 | it('Should fail when sync is disabled', async function () { | ||
89 | await withChannelSyncDisabled(async () => { | ||
90 | await command.create({ | ||
91 | token: server.accessToken, | ||
92 | attributes: baseCorrectParams, | ||
93 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
94 | }) | ||
95 | }) | ||
96 | }) | ||
97 | |||
98 | it('Should fail with nothing', async function () { | ||
99 | const fields = {} | ||
100 | await makePostBodyRequest({ | ||
101 | url: server.url, | ||
102 | path, | ||
103 | token: server.accessToken, | ||
104 | fields, | ||
105 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
106 | }) | ||
107 | }) | ||
108 | |||
109 | it('Should fail with no authentication', async function () { | ||
110 | await command.create({ | ||
111 | token: null, | ||
112 | attributes: baseCorrectParams, | ||
113 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401 | ||
114 | }) | ||
115 | }) | ||
116 | |||
117 | it('Should fail without a target url', async function () { | ||
118 | const attributes: VideoChannelSyncCreate = { | ||
119 | ...baseCorrectParams, | ||
120 | externalChannelUrl: null | ||
121 | } | ||
122 | await command.create({ | ||
123 | token: server.accessToken, | ||
124 | attributes, | ||
125 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
126 | }) | ||
127 | }) | ||
128 | |||
129 | it('Should fail without a channelId', async function () { | ||
130 | const attributes: VideoChannelSyncCreate = { | ||
131 | ...baseCorrectParams, | ||
132 | videoChannelId: null | ||
133 | } | ||
134 | await command.create({ | ||
135 | token: server.accessToken, | ||
136 | attributes, | ||
137 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
138 | }) | ||
139 | }) | ||
140 | |||
141 | it('Should fail with a channelId refering nothing', async function () { | ||
142 | const attributes: VideoChannelSyncCreate = { | ||
143 | ...baseCorrectParams, | ||
144 | videoChannelId: 42 | ||
145 | } | ||
146 | await command.create({ | ||
147 | token: server.accessToken, | ||
148 | attributes, | ||
149 | expectedStatus: HttpStatusCode.NOT_FOUND_404 | ||
150 | }) | ||
151 | }) | ||
152 | |||
153 | it('Should fail to create a sync when the user does not own the channel', async function () { | ||
154 | await command.create({ | ||
155 | token: userInfo.accessToken, | ||
156 | attributes: baseCorrectParams, | ||
157 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
158 | }) | ||
159 | }) | ||
160 | |||
161 | it('Should succeed to create a sync with root and for another user\'s channel', async function () { | ||
162 | const { videoChannelSync } = await command.create({ | ||
163 | token: server.accessToken, | ||
164 | attributes: { | ||
165 | ...baseCorrectParams, | ||
166 | videoChannelId: userInfo.channelId | ||
167 | }, | ||
168 | expectedStatus: HttpStatusCode.OK_200 | ||
169 | }) | ||
170 | userInfo.syncId = videoChannelSync.id | ||
171 | }) | ||
172 | |||
173 | it('Should succeed with the correct parameters', async function () { | ||
174 | const { videoChannelSync } = await command.create({ | ||
175 | token: server.accessToken, | ||
176 | attributes: baseCorrectParams, | ||
177 | expectedStatus: HttpStatusCode.OK_200 | ||
178 | }) | ||
179 | rootChannelSyncId = videoChannelSync.id | ||
180 | }) | ||
181 | |||
182 | it('Should fail when the user exceeds allowed number of synchronizations', async function () { | ||
183 | await withMaxSyncsPerUser(1, async () => { | ||
184 | await command.create({ | ||
185 | token: server.accessToken, | ||
186 | attributes: { | ||
187 | ...baseCorrectParams, | ||
188 | videoChannelId: userInfo.channelId | ||
189 | }, | ||
190 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
191 | }) | ||
192 | }) | ||
193 | }) | ||
194 | }) | ||
195 | |||
196 | describe('When listing my channel syncs', function () { | ||
197 | const myPath = '/api/v1/accounts/root/video-channel-syncs' | ||
198 | |||
199 | it('Should fail with a bad start pagination', async function () { | ||
200 | await checkBadStartPagination(server.url, myPath, server.accessToken) | ||
201 | }) | ||
202 | |||
203 | it('Should fail with a bad count pagination', async function () { | ||
204 | await checkBadCountPagination(server.url, myPath, server.accessToken) | ||
205 | }) | ||
206 | |||
207 | it('Should fail with an incorrect sort', async function () { | ||
208 | await checkBadSortPagination(server.url, myPath, server.accessToken) | ||
209 | }) | ||
210 | |||
211 | it('Should succeed with the correct parameters', async function () { | ||
212 | await command.listByAccount({ | ||
213 | accountName: 'root', | ||
214 | token: server.accessToken, | ||
215 | expectedStatus: HttpStatusCode.OK_200 | ||
216 | }) | ||
217 | }) | ||
218 | |||
219 | it('Should fail with no authentication', async function () { | ||
220 | await command.listByAccount({ | ||
221 | accountName: 'root', | ||
222 | token: null, | ||
223 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401 | ||
224 | }) | ||
225 | }) | ||
226 | |||
227 | it('Should fail when a simple user lists another user\'s synchronizations', async function () { | ||
228 | await command.listByAccount({ | ||
229 | accountName: 'root', | ||
230 | token: userInfo.accessToken, | ||
231 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
232 | }) | ||
233 | }) | ||
234 | |||
235 | it('Should succeed when root lists another user\'s synchronizations', async function () { | ||
236 | await command.listByAccount({ | ||
237 | accountName: userInfo.username, | ||
238 | token: server.accessToken, | ||
239 | expectedStatus: HttpStatusCode.OK_200 | ||
240 | }) | ||
241 | }) | ||
242 | |||
243 | it('Should succeed even with synchronization disabled', async function () { | ||
244 | await withChannelSyncDisabled(async function () { | ||
245 | await command.listByAccount({ | ||
246 | accountName: 'root', | ||
247 | token: server.accessToken, | ||
248 | expectedStatus: HttpStatusCode.OK_200 | ||
249 | }) | ||
250 | }) | ||
251 | }) | ||
252 | }) | ||
253 | |||
254 | describe('When triggering deletion', function () { | ||
255 | it('should fail with no authentication', async function () { | ||
256 | await command.delete({ | ||
257 | channelSyncId: userInfo.syncId, | ||
258 | token: null, | ||
259 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401 | ||
260 | }) | ||
261 | }) | ||
262 | |||
263 | it('Should fail when channelSyncId does not refer to any sync', async function () { | ||
264 | await command.delete({ | ||
265 | channelSyncId: 42, | ||
266 | token: server.accessToken, | ||
267 | expectedStatus: HttpStatusCode.NOT_FOUND_404 | ||
268 | }) | ||
269 | }) | ||
270 | |||
271 | it('Should fail when sync is not owned by the user', async function () { | ||
272 | await command.delete({ | ||
273 | channelSyncId: rootChannelSyncId, | ||
274 | token: userInfo.accessToken, | ||
275 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
276 | }) | ||
277 | }) | ||
278 | |||
279 | it('Should succeed when root delete a sync they do not own', async function () { | ||
280 | await command.delete({ | ||
281 | channelSyncId: userInfo.syncId, | ||
282 | token: server.accessToken, | ||
283 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
284 | }) | ||
285 | }) | ||
286 | |||
287 | it('should succeed when user delete a sync they own', async function () { | ||
288 | const { videoChannelSync } = await command.create({ | ||
289 | attributes: { | ||
290 | externalChannelUrl: FIXTURE_URLS.youtubeChannel, | ||
291 | videoChannelId: userInfo.channelId | ||
292 | }, | ||
293 | token: server.accessToken, | ||
294 | expectedStatus: HttpStatusCode.OK_200 | ||
295 | }) | ||
296 | |||
297 | await command.delete({ | ||
298 | channelSyncId: videoChannelSync.id, | ||
299 | token: server.accessToken, | ||
300 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
301 | }) | ||
302 | }) | ||
303 | |||
304 | it('Should succeed even when synchronization is disabled', async function () { | ||
305 | await withChannelSyncDisabled(async function () { | ||
306 | await command.delete({ | ||
307 | channelSyncId: rootChannelSyncId, | ||
308 | token: server.accessToken, | ||
309 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
310 | }) | ||
311 | }) | ||
312 | }) | ||
313 | }) | ||
314 | |||
315 | after(async function () { | ||
316 | await server?.kill() | ||
317 | }) | ||
318 | }) | ||