aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/tests/src/api/runners/runner-common.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-07-31 14:34:36 +0200
committerChocobozzz <me@florianbigard.com>2023-08-11 15:02:33 +0200
commit3a4992633ee62d5edfbb484d9c6bcb3cf158489d (patch)
treee4510b39bdac9c318fdb4b47018d08f15368b8f0 /packages/tests/src/api/runners/runner-common.ts
parent04d1da5621d25d59bd5fa1543b725c497bf5d9a8 (diff)
downloadPeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.gz
PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.zst
PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.zip
Migrate server to ESM
Sorry for the very big commit that may lead to git log issues and merge conflicts, but it's a major step forward: * Server can be faster at startup because imports() are async and we can easily lazy import big modules * Angular doesn't seem to support ES import (with .js extension), so we had to correctly organize peertube into a monorepo: * Use yarn workspace feature * Use typescript reference projects for dependencies * Shared projects have been moved into "packages", each one is now a node module (with a dedicated package.json/tsconfig.json) * server/tools have been moved into apps/ and is now a dedicated app bundled and published on NPM so users don't have to build peertube cli tools manually * server/tests have been moved into packages/ so we don't compile them every time we want to run the server * Use isolatedModule option: * Had to move from const enum to const (https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums) * Had to explictely specify "type" imports when used in decorators * Prefer tsx (that uses esbuild under the hood) instead of ts-node to load typescript files (tests with mocha or scripts): * To reduce test complexity as esbuild doesn't support decorator metadata, we only test server files that do not import server models * We still build tests files into js files for a faster CI * Remove unmaintained peertube CLI import script * Removed some barrels to speed up execution (less imports)
Diffstat (limited to 'packages/tests/src/api/runners/runner-common.ts')
-rw-r--r--packages/tests/src/api/runners/runner-common.ts744
1 files changed, 744 insertions, 0 deletions
diff --git a/packages/tests/src/api/runners/runner-common.ts b/packages/tests/src/api/runners/runner-common.ts
new file mode 100644
index 000000000..53ea321d0
--- /dev/null
+++ b/packages/tests/src/api/runners/runner-common.ts
@@ -0,0 +1,744 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { wait } from '@peertube/peertube-core-utils'
4import {
5 HttpStatusCode,
6 Runner,
7 RunnerJob,
8 RunnerJobAdmin,
9 RunnerJobState,
10 RunnerJobStateType,
11 RunnerJobVODWebVideoTranscodingPayload,
12 RunnerRegistrationToken
13} from '@peertube/peertube-models'
14import {
15 PeerTubeServer,
16 cleanupTests,
17 createSingleServer,
18 setAccessTokensToServers,
19 setDefaultVideoChannel,
20 waitJobs
21} from '@peertube/peertube-server-commands'
22import { expect } from 'chai'
23
24describe('Test runner common actions', function () {
25 let server: PeerTubeServer
26 let registrationToken: string
27 let runnerToken: string
28 let jobMaxPriority: string
29
30 before(async function () {
31 this.timeout(120_000)
32
33 server = await createSingleServer(1, {
34 remote_runners: {
35 stalled_jobs: {
36 vod: '5 seconds'
37 }
38 }
39 })
40
41 await setAccessTokensToServers([ server ])
42 await setDefaultVideoChannel([ server ])
43
44 await server.config.enableTranscoding({ hls: true, webVideo: true })
45 await server.config.enableRemoteTranscoding()
46 })
47
48 describe('Managing runner registration tokens', function () {
49 let base: RunnerRegistrationToken[]
50 let registrationTokenToDelete: RunnerRegistrationToken
51
52 it('Should have a default registration token', async function () {
53 const { total, data } = await server.runnerRegistrationTokens.list()
54
55 expect(total).to.equal(1)
56 expect(data).to.have.lengthOf(1)
57
58 const token = data[0]
59 expect(token.id).to.exist
60 expect(token.createdAt).to.exist
61 expect(token.updatedAt).to.exist
62 expect(token.registeredRunnersCount).to.equal(0)
63 expect(token.registrationToken).to.exist
64 })
65
66 it('Should create other registration tokens', async function () {
67 await server.runnerRegistrationTokens.generate()
68 await server.runnerRegistrationTokens.generate()
69
70 const { total, data } = await server.runnerRegistrationTokens.list()
71 expect(total).to.equal(3)
72 expect(data).to.have.lengthOf(3)
73 })
74
75 it('Should list registration tokens', async function () {
76 {
77 const { total, data } = await server.runnerRegistrationTokens.list({ sort: 'createdAt' })
78 expect(total).to.equal(3)
79 expect(data).to.have.lengthOf(3)
80 expect(new Date(data[0].createdAt)).to.be.below(new Date(data[1].createdAt))
81 expect(new Date(data[1].createdAt)).to.be.below(new Date(data[2].createdAt))
82
83 base = data
84
85 registrationTokenToDelete = data[0]
86 registrationToken = data[1].registrationToken
87 }
88
89 {
90 const { total, data } = await server.runnerRegistrationTokens.list({ sort: '-createdAt', start: 2, count: 1 })
91 expect(total).to.equal(3)
92 expect(data).to.have.lengthOf(1)
93 expect(data[0].registrationToken).to.equal(base[0].registrationToken)
94 }
95 })
96
97 it('Should have appropriate registeredRunnersCount for registration tokens', async function () {
98 await server.runners.register({ name: 'to delete 1', registrationToken: registrationTokenToDelete.registrationToken })
99 await server.runners.register({ name: 'to delete 2', registrationToken: registrationTokenToDelete.registrationToken })
100
101 const { data } = await server.runnerRegistrationTokens.list()
102
103 for (const d of data) {
104 if (d.registrationToken === registrationTokenToDelete.registrationToken) {
105 expect(d.registeredRunnersCount).to.equal(2)
106 } else {
107 expect(d.registeredRunnersCount).to.equal(0)
108 }
109 }
110
111 const { data: runners } = await server.runners.list()
112 expect(runners).to.have.lengthOf(2)
113 })
114
115 it('Should delete a registration token', async function () {
116 await server.runnerRegistrationTokens.delete({ id: registrationTokenToDelete.id })
117
118 const { total, data } = await server.runnerRegistrationTokens.list({ sort: 'createdAt' })
119 expect(total).to.equal(2)
120 expect(data).to.have.lengthOf(2)
121
122 for (const d of data) {
123 expect(d.registeredRunnersCount).to.equal(0)
124 expect(d.registrationToken).to.not.equal(registrationTokenToDelete.registrationToken)
125 }
126 })
127
128 it('Should have removed runners of this registration token', async function () {
129 const { data: runners } = await server.runners.list()
130 expect(runners).to.have.lengthOf(0)
131 })
132 })
133
134 describe('Managing runners', function () {
135 let toDelete: Runner
136
137 it('Should not have runners available', async function () {
138 const { total, data } = await server.runners.list()
139
140 expect(data).to.have.lengthOf(0)
141 expect(total).to.equal(0)
142 })
143
144 it('Should register runners', async function () {
145 const now = new Date()
146
147 const result = await server.runners.register({
148 name: 'runner 1',
149 description: 'my super runner 1',
150 registrationToken
151 })
152 expect(result.runnerToken).to.exist
153 runnerToken = result.runnerToken
154
155 await server.runners.register({
156 name: 'runner 2',
157 registrationToken
158 })
159
160 const { total, data } = await server.runners.list({ sort: 'createdAt' })
161 expect(total).to.equal(2)
162 expect(data).to.have.lengthOf(2)
163
164 for (const d of data) {
165 expect(d.id).to.exist
166 expect(d.createdAt).to.exist
167 expect(d.updatedAt).to.exist
168 expect(new Date(d.createdAt)).to.be.above(now)
169 expect(new Date(d.updatedAt)).to.be.above(now)
170 expect(new Date(d.lastContact)).to.be.above(now)
171 expect(d.ip).to.exist
172 }
173
174 expect(data[0].name).to.equal('runner 1')
175 expect(data[0].description).to.equal('my super runner 1')
176
177 expect(data[1].name).to.equal('runner 2')
178 expect(data[1].description).to.be.null
179
180 toDelete = data[1]
181 })
182
183 it('Should list runners', async function () {
184 const { total, data } = await server.runners.list({ sort: '-createdAt', start: 1, count: 1 })
185
186 expect(total).to.equal(2)
187 expect(data).to.have.lengthOf(1)
188 expect(data[0].name).to.equal('runner 1')
189 })
190
191 it('Should delete a runner', async function () {
192 await server.runners.delete({ id: toDelete.id })
193
194 const { total, data } = await server.runners.list()
195
196 expect(total).to.equal(1)
197 expect(data).to.have.lengthOf(1)
198 expect(data[0].name).to.equal('runner 1')
199 })
200
201 it('Should unregister a runner', async function () {
202 const registered = await server.runners.autoRegisterRunner()
203
204 {
205 const { total, data } = await server.runners.list()
206 expect(total).to.equal(2)
207 expect(data).to.have.lengthOf(2)
208 }
209
210 await server.runners.unregister({ runnerToken: registered })
211
212 {
213 const { total, data } = await server.runners.list()
214 expect(total).to.equal(1)
215 expect(data).to.have.lengthOf(1)
216 expect(data[0].name).to.equal('runner 1')
217 }
218 })
219 })
220
221 describe('Managing runner jobs', function () {
222 let jobUUID: string
223 let jobToken: string
224 let lastRunnerContact: Date
225 let failedJob: RunnerJob
226
227 async function checkMainJobState (
228 mainJobState: RunnerJobStateType,
229 otherJobStates: RunnerJobStateType[] = [ RunnerJobState.PENDING, RunnerJobState.WAITING_FOR_PARENT_JOB ]
230 ) {
231 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
232
233 for (const job of data) {
234 if (job.uuid === jobUUID) {
235 expect(job.state.id).to.equal(mainJobState)
236 } else {
237 expect(otherJobStates).to.include(job.state.id)
238 }
239 }
240 }
241
242 function getMainJob () {
243 return server.runnerJobs.getJob({ uuid: jobUUID })
244 }
245
246 describe('List jobs', function () {
247
248 it('Should not have jobs', async function () {
249 const { total, data } = await server.runnerJobs.list()
250
251 expect(data).to.have.lengthOf(0)
252 expect(total).to.equal(0)
253 })
254
255 it('Should upload a video and have available jobs', async function () {
256 await server.videos.quickUpload({ name: 'to transcode' })
257 await waitJobs([ server ])
258
259 const { total, data } = await server.runnerJobs.list()
260
261 expect(data).to.have.lengthOf(10)
262 expect(total).to.equal(10)
263
264 for (const job of data) {
265 expect(job.startedAt).to.not.exist
266 expect(job.finishedAt).to.not.exist
267 expect(job.payload).to.exist
268 expect(job.privatePayload).to.exist
269 }
270
271 const hlsJobs = data.filter(d => d.type === 'vod-hls-transcoding')
272 const webVideoJobs = data.filter(d => d.type === 'vod-web-video-transcoding')
273
274 expect(hlsJobs).to.have.lengthOf(5)
275 expect(webVideoJobs).to.have.lengthOf(5)
276
277 const pendingJobs = data.filter(d => d.state.id === RunnerJobState.PENDING)
278 const waitingJobs = data.filter(d => d.state.id === RunnerJobState.WAITING_FOR_PARENT_JOB)
279
280 expect(pendingJobs).to.have.lengthOf(1)
281 expect(waitingJobs).to.have.lengthOf(9)
282 })
283
284 it('Should upload another video and list/sort jobs', async function () {
285 await server.videos.quickUpload({ name: 'to transcode 2' })
286 await waitJobs([ server ])
287
288 {
289 const { total, data } = await server.runnerJobs.list({ start: 0, count: 30 })
290
291 expect(data).to.have.lengthOf(20)
292 expect(total).to.equal(20)
293
294 jobUUID = data[16].uuid
295 }
296
297 {
298 const { total, data } = await server.runnerJobs.list({ start: 3, count: 1, sort: 'createdAt' })
299 expect(total).to.equal(20)
300
301 expect(data).to.have.lengthOf(1)
302 expect(data[0].uuid).to.equal(jobUUID)
303 }
304
305 {
306 let previousPriority = Infinity
307 const { total, data } = await server.runnerJobs.list({ start: 0, count: 100, sort: '-priority' })
308 expect(total).to.equal(20)
309
310 for (const job of data) {
311 expect(job.priority).to.be.at.most(previousPriority)
312 previousPriority = job.priority
313
314 if (job.state.id === RunnerJobState.PENDING) {
315 jobMaxPriority = job.uuid
316 }
317 }
318 }
319 })
320
321 it('Should search jobs', async function () {
322 {
323 const { total, data } = await server.runnerJobs.list({ search: jobUUID })
324
325 expect(data).to.have.lengthOf(1)
326 expect(total).to.equal(1)
327
328 expect(data[0].uuid).to.equal(jobUUID)
329 }
330
331 {
332 const { total, data } = await server.runnerJobs.list({ search: 'toto' })
333
334 expect(data).to.have.lengthOf(0)
335 expect(total).to.equal(0)
336 }
337
338 {
339 const { total, data } = await server.runnerJobs.list({ search: 'hls' })
340
341 expect(data).to.not.have.lengthOf(0)
342 expect(total).to.not.equal(0)
343
344 for (const job of data) {
345 expect(job.type).to.include('hls')
346 }
347 }
348 })
349
350 it('Should filter jobs', async function () {
351 {
352 const { total, data } = await server.runnerJobs.list({ stateOneOf: [ RunnerJobState.WAITING_FOR_PARENT_JOB ] })
353
354 expect(data).to.not.have.lengthOf(0)
355 expect(total).to.not.equal(0)
356
357 for (const job of data) {
358 expect(job.state.label).to.equal('Waiting for parent job to finish')
359 }
360 }
361
362 {
363 const { total, data } = await server.runnerJobs.list({ stateOneOf: [ RunnerJobState.COMPLETED ] })
364
365 expect(data).to.have.lengthOf(0)
366 expect(total).to.equal(0)
367 }
368 })
369 })
370
371 describe('Accept/update/abort/process a job', function () {
372
373 it('Should request available jobs', async function () {
374 lastRunnerContact = new Date()
375
376 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
377
378 // Only optimize jobs are available
379 expect(availableJobs).to.have.lengthOf(2)
380
381 for (const job of availableJobs) {
382 expect(job.uuid).to.exist
383 expect(job.payload.input).to.exist
384 expect((job.payload as RunnerJobVODWebVideoTranscodingPayload).output).to.exist
385
386 expect((job as RunnerJobAdmin).privatePayload).to.not.exist
387 }
388
389 const hlsJobs = availableJobs.filter(d => d.type === 'vod-hls-transcoding')
390 const webVideoJobs = availableJobs.filter(d => d.type === 'vod-web-video-transcoding')
391
392 expect(hlsJobs).to.have.lengthOf(0)
393 expect(webVideoJobs).to.have.lengthOf(2)
394
395 jobUUID = webVideoJobs[0].uuid
396 })
397
398 it('Should have sorted available jobs by priority', async function () {
399 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
400
401 expect(availableJobs[0].uuid).to.equal(jobMaxPriority)
402 })
403
404 it('Should have last runner contact updated', async function () {
405 await wait(1000)
406
407 const { data } = await server.runners.list({ sort: 'createdAt' })
408 expect(new Date(data[0].lastContact)).to.be.above(lastRunnerContact)
409 })
410
411 it('Should accept a job', async function () {
412 const startedAt = new Date()
413
414 const { job } = await server.runnerJobs.accept({ runnerToken, jobUUID })
415 jobToken = job.jobToken
416
417 const checkProcessingJob = (job: RunnerJob & { jobToken?: string }, fromAccept: boolean) => {
418 expect(job.uuid).to.equal(jobUUID)
419
420 expect(job.type).to.equal('vod-web-video-transcoding')
421 expect(job.state.label).to.equal('Processing')
422 expect(job.state.id).to.equal(RunnerJobState.PROCESSING)
423
424 expect(job.runner).to.exist
425 expect(job.runner.name).to.equal('runner 1')
426 expect(job.runner.description).to.equal('my super runner 1')
427
428 expect(job.progress).to.be.null
429
430 expect(job.startedAt).to.exist
431 expect(new Date(job.startedAt)).to.be.above(startedAt)
432
433 expect(job.finishedAt).to.not.exist
434
435 expect(job.failures).to.equal(0)
436
437 expect(job.payload).to.exist
438
439 if (fromAccept) {
440 expect(job.jobToken).to.exist
441 expect((job as RunnerJobAdmin).privatePayload).to.not.exist
442 } else {
443 expect(job.jobToken).to.not.exist
444 expect((job as RunnerJobAdmin).privatePayload).to.exist
445 }
446 }
447
448 checkProcessingJob(job, true)
449
450 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
451
452 const processingJob = data.find(j => j.uuid === jobUUID)
453 checkProcessingJob(processingJob, false)
454
455 await checkMainJobState(RunnerJobState.PROCESSING)
456 })
457
458 it('Should update a job', async function () {
459 await server.runnerJobs.update({ runnerToken, jobUUID, jobToken, progress: 53 })
460
461 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
462
463 for (const job of data) {
464 if (job.state.id === RunnerJobState.PROCESSING) {
465 expect(job.progress).to.equal(53)
466 } else {
467 expect(job.progress).to.be.null
468 }
469 }
470 })
471
472 it('Should abort a job', async function () {
473 await server.runnerJobs.abort({ runnerToken, jobUUID, jobToken, reason: 'for tests' })
474
475 await checkMainJobState(RunnerJobState.PENDING)
476
477 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
478 for (const job of data) {
479 expect(job.progress).to.be.null
480 }
481 })
482
483 it('Should accept the same job again and post a success', async function () {
484 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
485 expect(availableJobs.find(j => j.uuid === jobUUID)).to.exist
486
487 const { job } = await server.runnerJobs.accept({ runnerToken, jobUUID })
488 jobToken = job.jobToken
489
490 await checkMainJobState(RunnerJobState.PROCESSING)
491
492 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
493
494 for (const job of data) {
495 expect(job.progress).to.be.null
496 }
497
498 const payload = {
499 videoFile: 'video_short.mp4'
500 }
501
502 await server.runnerJobs.success({ runnerToken, jobUUID, jobToken, payload })
503 })
504
505 it('Should not have available jobs anymore', async function () {
506 await checkMainJobState(RunnerJobState.COMPLETED)
507
508 const job = await getMainJob()
509 expect(job.finishedAt).to.exist
510
511 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
512 expect(availableJobs.find(j => j.uuid === jobUUID)).to.not.exist
513 })
514 })
515
516 describe('Error job', function () {
517
518 it('Should accept another job and post an error', async function () {
519 await server.runnerJobs.cancelAllJobs()
520 await server.videos.quickUpload({ name: 'video' })
521 await waitJobs([ server ])
522
523 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
524 jobUUID = availableJobs[0].uuid
525
526 const { job } = await server.runnerJobs.accept({ runnerToken, jobUUID })
527 jobToken = job.jobToken
528
529 await server.runnerJobs.error({ runnerToken, jobUUID, jobToken, message: 'Error' })
530 })
531
532 it('Should have job failures increased', async function () {
533 const job = await getMainJob()
534 expect(job.state.id).to.equal(RunnerJobState.PENDING)
535 expect(job.failures).to.equal(1)
536 expect(job.error).to.be.null
537 expect(job.progress).to.be.null
538 expect(job.finishedAt).to.not.exist
539 })
540
541 it('Should error a job when job attempts is too big', async function () {
542 for (let i = 0; i < 4; i++) {
543 const { job } = await server.runnerJobs.accept({ runnerToken, jobUUID })
544 jobToken = job.jobToken
545
546 await server.runnerJobs.error({ runnerToken, jobUUID, jobToken, message: 'Error ' + i })
547 }
548
549 const job = await getMainJob()
550 expect(job.failures).to.equal(5)
551 expect(job.state.id).to.equal(RunnerJobState.ERRORED)
552 expect(job.state.label).to.equal('Errored')
553 expect(job.error).to.equal('Error 3')
554 expect(job.progress).to.be.null
555 expect(job.finishedAt).to.exist
556
557 failedJob = job
558 })
559
560 it('Should have failed children jobs too', async function () {
561 const { data } = await server.runnerJobs.list({ count: 50, sort: '-updatedAt' })
562
563 const children = data.filter(j => j.parent?.uuid === failedJob.uuid)
564 expect(children).to.have.lengthOf(9)
565
566 for (const child of children) {
567 expect(child.parent.uuid).to.equal(failedJob.uuid)
568 expect(child.parent.type).to.equal(failedJob.type)
569 expect(child.parent.state.id).to.equal(failedJob.state.id)
570 expect(child.parent.state.label).to.equal(failedJob.state.label)
571
572 expect(child.state.id).to.equal(RunnerJobState.PARENT_ERRORED)
573 expect(child.state.label).to.equal('Parent job failed')
574 }
575 })
576 })
577
578 describe('Cancel', function () {
579
580 it('Should cancel a pending job', async function () {
581 await server.videos.quickUpload({ name: 'video' })
582 await waitJobs([ server ])
583
584 {
585 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
586
587 const pendingJob = data.find(j => j.state.id === RunnerJobState.PENDING)
588 jobUUID = pendingJob.uuid
589
590 await server.runnerJobs.cancelByAdmin({ jobUUID })
591 }
592
593 {
594 const job = await getMainJob()
595 expect(job.state.id).to.equal(RunnerJobState.CANCELLED)
596 expect(job.state.label).to.equal('Cancelled')
597 }
598
599 {
600 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
601 const children = data.filter(j => j.parent?.uuid === jobUUID)
602 expect(children).to.have.lengthOf(9)
603
604 for (const child of children) {
605 expect(child.state.id).to.equal(RunnerJobState.PARENT_CANCELLED)
606 }
607 }
608 })
609
610 it('Should cancel an already accepted job and skip success/error', async function () {
611 await server.videos.quickUpload({ name: 'video' })
612 await waitJobs([ server ])
613
614 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
615 jobUUID = availableJobs[0].uuid
616
617 const { job } = await server.runnerJobs.accept({ runnerToken, jobUUID })
618 jobToken = job.jobToken
619
620 await server.runnerJobs.cancelByAdmin({ jobUUID })
621
622 await server.runnerJobs.abort({ runnerToken, jobUUID, jobToken, reason: 'aborted', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
623 })
624 })
625
626 describe('Remove', function () {
627
628 it('Should remove a pending job', async function () {
629 await server.videos.quickUpload({ name: 'video' })
630 await waitJobs([ server ])
631
632 {
633 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
634
635 const pendingJob = data.find(j => j.state.id === RunnerJobState.PENDING)
636 jobUUID = pendingJob.uuid
637
638 await server.runnerJobs.deleteByAdmin({ jobUUID })
639 }
640
641 {
642 const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' })
643
644 const parent = data.find(j => j.uuid === jobUUID)
645 expect(parent).to.not.exist
646
647 const children = data.filter(j => j.parent?.uuid === jobUUID)
648 expect(children).to.have.lengthOf(0)
649 }
650 })
651 })
652
653 describe('Stalled jobs', function () {
654
655 it('Should abort stalled jobs', async function () {
656 this.timeout(60000)
657
658 await server.videos.quickUpload({ name: 'video' })
659 await server.videos.quickUpload({ name: 'video' })
660 await waitJobs([ server ])
661
662 const { job: job1 } = await server.runnerJobs.autoAccept({ runnerToken })
663 const { job: stalledJob } = await server.runnerJobs.autoAccept({ runnerToken })
664
665 for (let i = 0; i < 6; i++) {
666 await wait(2000)
667
668 await server.runnerJobs.update({ runnerToken, jobToken: job1.jobToken, jobUUID: job1.uuid })
669 }
670
671 const refreshedJob1 = await server.runnerJobs.getJob({ uuid: job1.uuid })
672 const refreshedStalledJob = await server.runnerJobs.getJob({ uuid: stalledJob.uuid })
673
674 expect(refreshedJob1.state.id).to.equal(RunnerJobState.PROCESSING)
675 expect(refreshedStalledJob.state.id).to.equal(RunnerJobState.PENDING)
676 })
677 })
678
679 describe('Rate limit', function () {
680
681 before(async function () {
682 this.timeout(60000)
683
684 await server.kill()
685
686 await server.run({
687 rates_limit: {
688 api: {
689 max: 10
690 }
691 }
692 })
693 })
694
695 it('Should rate limit an unknown runner, but not a registered one', async function () {
696 this.timeout(60000)
697
698 await server.videos.quickUpload({ name: 'video' })
699 await waitJobs([ server ])
700
701 const { job } = await server.runnerJobs.autoAccept({ runnerToken })
702
703 for (let i = 0; i < 20; i++) {
704 try {
705 await server.runnerJobs.request({ runnerToken })
706 await server.runnerJobs.update({ runnerToken, jobToken: job.jobToken, jobUUID: job.uuid })
707 } catch {}
708 }
709
710 // Invalid
711 {
712 await server.runnerJobs.request({ runnerToken: 'toto', expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
713 await server.runnerJobs.update({
714 runnerToken: 'toto',
715 jobToken: job.jobToken,
716 jobUUID: job.uuid,
717 expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429
718 })
719 }
720
721 // Not provided
722 {
723 await server.runnerJobs.request({ runnerToken: undefined, expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429 })
724 await server.runnerJobs.update({
725 runnerToken: undefined,
726 jobToken: job.jobToken,
727 jobUUID: job.uuid,
728 expectedStatus: HttpStatusCode.TOO_MANY_REQUESTS_429
729 })
730 }
731
732 // Registered
733 {
734 await server.runnerJobs.request({ runnerToken })
735 await server.runnerJobs.update({ runnerToken, jobToken: job.jobToken, jobUUID: job.uuid })
736 }
737 })
738 })
739 })
740
741 after(async function () {
742 await cleanupTests([ server ])
743 })
744})