Best JavaScript code snippet using tracetest
queue.test.js
Source:queue.test.js
1// @flow2import { v4 as uuidv4 } from 'uuid';3import { AbortError, FatalError } from '../src/errors';4import {5 jobEmitter,6 getJobsInQueueFromDatabase,7 getCleanupsInQueueFromDatabase,8 silentlyRemoveJobFromDatabase,9 silentlyRemoveQueueFromDatabase,10 importJobsAndCleanups,11 removeJobFromDatabase,12 enqueueToDatabase,13 getCompletedJobsCountFromDatabase,14 removeCompletedExpiredItemsFromDatabase,15 markCleanupStartAfterInDatabase,16 markJobCleanupAndRemoveInDatabase,17 JOB_ERROR_STATUS,18 JOB_CLEANUP_STATUS,19 JOB_CLEANUP_AND_REMOVE_STATUS,20 JOB_COMPLETE_STATUS,21 JOB_ABORTED_STATUS,22 QUEUE_ERROR_STATUS,23 QUEUE_COMPLETE_STATUS,24 getQueueStatus,25} from '../src/database';26import {27 CLEANUP_JOB_TYPE,28} from '../src/queue';29import {30 TRIGGER_NO_ERROR,31 TRIGGER_ERROR,32 TRIGGER_FATAL_ERROR,33 TRIGGER_ERROR_IN_CLEANUP,34 TRIGGER_FATAL_ERROR_IN_CLEANUP,35 TRIGGER_100MS_DELAY,36 TRIGGER_ABORT_ERROR,37 TRIGGER_HANDLER_RETURN_FALSE,38 emitter as echoEmitter,39} from './lib/echo-handler';40import { asyncEmitMatchers } from './lib/emit';41import { queue } from './lib/queue';42jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;43describe('Queue', () => {44 beforeAll(() => {45 jasmine.addAsyncMatchers(asyncEmitMatchers);46 queue.enableStartOnJob();47 });48 afterAll(() => {49 queue.disableStartOnJob();50 });51 afterEach(async () => {52 await queue.clear();53 queue.enableStartOnJob();54 });55 it('Enqueues to the database and is handled', async () => {56 const queueId = uuidv4();57 const value = uuidv4();58 const args = [TRIGGER_NO_ERROR, value];59 const id = await enqueueToDatabase(queueId, 'echo', args);60 await expectAsync(queue).toEmit('dequeue', { id });61 await expectAsync(echoEmitter).toEmit('echo', { value });62 });63 it('Executes jobs that are prioritized at the beginning of the queue and cleans up prioritized jobs at the end of the queue', async () => {64 queue.disableStartOnJob();65 const queueId = uuidv4();66 const valueA = uuidv4();67 const valueB = uuidv4();68 const valueC = uuidv4();69 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);70 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueB]);71 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueC], { prioritize: true });72 queue.dequeue();73 await expectAsync(echoEmitter).toEmit('echo', { value: valueC });74 await expectAsync(echoEmitter).toEmit('echo', { value: valueA });75 await expectAsync(echoEmitter).toEmit('echo', { value: valueB });76 await queue.onIdle();77 queue.abortQueue(queueId);78 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueB, cleanupData: { value: valueB } });79 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueA, cleanupData: { value: valueA } });80 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueC, cleanupData: { value: valueC } });81 queue.enableStartOnJob();82 });83 it('Gets active queueIds', async () => {84 const queueId = uuidv4();85 const value = uuidv4();86 const args = [TRIGGER_NO_ERROR, value];87 await enqueueToDatabase(queueId, 'echo', args);88 await expectAsync(queue.getQueueIds()).toBeResolvedTo(new Set([queueId]));89 await queue.onIdle();90 await queue.clear();91 await expectAsync(queue.getQueueIds()).toBeResolvedTo(new Set([]));92 });93 it('Gets the current job type', async () => {94 queue.disableStartOnJob();95 const queueId = uuidv4();96 const value = uuidv4();97 await enqueueToDatabase(queueId, 'echo', [TRIGGER_100MS_DELAY, value]);98 expect(queue.getCurrentJobType(queueId)).toBeUndefined();99 queue.dequeue();100 await expectAsync(queue).toEmit('queueJobType', queueId, 'echo');101 expect(queue.getCurrentJobType(queueId)).toEqual('echo');102 await expectAsync(queue).toEmit('queueJobType', queueId, undefined);103 expect(queue.getCurrentJobType(queueId)).toBeUndefined();104 await queue.onIdle();105 await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);106 queue.dequeue();107 await expectAsync(queue).toEmit('queueJobType', queueId, 'echo');108 expect(queue.getCurrentJobType(queueId)).toEqual('echo');109 await expectAsync(queue).toEmit('queueJobType', queueId, CLEANUP_JOB_TYPE);110 expect(queue.getCurrentJobType(queueId)).toEqual(CLEANUP_JOB_TYPE);111 await expectAsync(queue).toEmit('queueJobType', queueId, undefined);112 expect(queue.getCurrentJobType(queueId)).toBeUndefined();113 });114 it('Enqueues to the database and is cleaned up after an error without retrying', async () => {115 const queueId = uuidv4();116 const value = uuidv4();117 let retries = 0;118 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);119 const handleRetry = ({ id: retryId }) => {120 if (retryId === id) {121 retries += 1;122 }123 };124 queue.addListener('retry', handleRetry);125 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });126 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });127 expect(retries).toEqual(0);128 queue.removeListener('retry', handleRetry);129 });130 it('Cleans up completed items in the queue if the handler throws an AbortError', async () => {131 const queueId = uuidv4();132 const valueA = uuidv4();133 const valueB = uuidv4();134 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);135 const idB = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ABORT_ERROR, valueB]);136 await expectAsync(echoEmitter).toEmit('echo', { value: valueA });137 await expectAsync(queue).toEmit('fatalError', { queueId, id: idB, error: jasmine.any(AbortError) });138 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueB, cleanupData: { value: valueB } });139 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueA, cleanupData: { value: valueA } });140 });141 it('Emits jobUpdate and jobDelete events, then removes a job from the database if the handler returns false', async () => {142 const queueId = uuidv4();143 const value = uuidv4();144 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_HANDLER_RETURN_FALSE, value], { delay: 100 });145 const jobUpdatePromise = expectAsync(jobEmitter).toEmit('jobUpdate', id, queueId, 'echo', JOB_COMPLETE_STATUS);146 const jobDeletePromise = expectAsync(jobEmitter).toEmit('jobDelete', id, queueId);147 await jobUpdatePromise;148 await jobDeletePromise;149 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);150 });151 it('Emits fatalError if the queue is aborted before starting', async () => {152 const queueId = uuidv4();153 const value = uuidv4();154 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, value], { delay: 100 });155 await expectAsync(queue).toEmit('dequeue', { id });156 queue.abortQueue(queueId);157 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(AbortError) });158 });159 it('Emits fatalError if the queue is aborted while running', async () => {160 const queueId = uuidv4();161 const value = uuidv4();162 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_100MS_DELAY, value]);163 // Jobs are put into error status in case the process stops during execution164 // to trigger subsequent cleanup165 await expectAsync(jobEmitter).toEmit('jobUpdate', id, queueId, 'echo', JOB_ERROR_STATUS);166 queue.abortQueue(queueId);167 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(AbortError) });168 });169 it('Aborts retry delay', async () => {170 const queueId = uuidv4();171 const value = uuidv4();172 queue.setRetryJobDelay('echo', (attempt, error) => {173 expect(attempt).toEqual(1);174 expect(error).toBeInstanceOf(Error);175 return 120000;176 });177 await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);178 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });179 await queue.abortQueue(queueId);180 await queue.onIdle();181 queue.removeRetryJobDelay('echo');182 });183 it('Enqueues to the database and is cleaned up after an error without retrying if the retry delay function returns false', async () => {184 const queueId = uuidv4();185 const value = uuidv4();186 let retries = 0;187 queue.setRetryJobDelay('echo', (attempt, error) => {188 expect(attempt).toEqual(1);189 expect(error).toBeInstanceOf(Error);190 return false;191 });192 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);193 const handleRetry = ({ id: retryId }) => {194 if (retryId === id) {195 retries += 1;196 }197 };198 queue.addListener('retry', handleRetry);199 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });200 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });201 expect(retries).toEqual(0);202 queue.removeListener('retry', handleRetry);203 queue.removeRetryJobDelay('echo');204 });205 it('Enqueues to the database and is cleaned up after an error without retrying if the retry delay function throws an error', async () => {206 const queueId = uuidv4();207 const value = uuidv4();208 let retries = 0;209 queue.setRetryJobDelay('echo', (attempt, error) => {210 expect(attempt).toEqual(1);211 expect(error).toBeInstanceOf(Error);212 throw new Error('RetryJobDelay synchronous error');213 });214 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);215 const handleRetry = ({ id: retryId }) => {216 if (retryId === id) {217 retries += 1;218 }219 };220 queue.addListener('retry', handleRetry);221 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });222 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });223 expect(retries).toEqual(0);224 queue.removeListener('retry', handleRetry);225 queue.removeRetryJobDelay('echo');226 });227 it('Enqueues to the database and is cleaned up after an error without retrying if an asynchronous retry delay function throws an error', async () => {228 const queueId = uuidv4();229 const value = uuidv4();230 let retries = 0;231 queue.setRetryJobDelay('echo', async (attempt, error) => {232 expect(attempt).toEqual(1);233 expect(error).toBeInstanceOf(Error);234 await new Promise((resolve) => setTimeout(resolve, 10));235 throw new Error('RetryJobDelay asynchronous error');236 });237 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);238 const handleRetry = ({ id: retryId }) => {239 if (retryId === id) {240 retries += 1;241 }242 };243 queue.addListener('retry', handleRetry);244 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });245 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });246 expect(retries).toEqual(0);247 queue.removeListener('retry', handleRetry);248 queue.removeRetryJobDelay('echo');249 });250 it('Enqueues to the database and is cleaned up after an error, retrying once if the retry delay function returns 0', async () => {251 const queueId = uuidv4();252 const value = uuidv4();253 let retries = 0;254 let didRequestRetry = false;255 queue.setRetryJobDelay('echo', (attempt, error) => {256 if (!didRequestRetry) {257 expect(attempt).toEqual(1);258 expect(error).toBeInstanceOf(Error);259 didRequestRetry = true;260 return 0;261 }262 expect(attempt).toEqual(2);263 expect(error).toBeInstanceOf(Error);264 return false;265 });266 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);267 const handleRetry = ({ id: retryId }) => {268 if (retryId === id) {269 retries += 1;270 }271 };272 queue.addListener('retry', handleRetry);273 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });274 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });275 expect(retries).toEqual(1);276 queue.removeListener('retry', handleRetry);277 queue.removeRetryJobDelay('echo');278 });279 it('Enqueues to the database and is cleaned up after an error without retrying if the asynchronous retry delay function returns false', async () => {280 const queueId = uuidv4();281 const value = uuidv4();282 let retries = 0;283 queue.setRetryJobDelay('echo', async (attempt, error) => {284 expect(attempt).toEqual(1);285 expect(error).toBeInstanceOf(Error);286 await new Promise((resolve) => setTimeout(resolve, 10));287 return false;288 });289 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);290 const handleRetry = ({ id: retryId }) => {291 if (retryId === id) {292 retries += 1;293 }294 };295 queue.addListener('retry', handleRetry);296 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });297 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });298 expect(retries).toEqual(0);299 queue.removeListener('retry', handleRetry);300 queue.removeRetryJobDelay('echo');301 });302 it('Enqueues to the database and is cleaned up after an error, retrying once if the asynchronous retry delay function returns 0', async () => {303 const queueId = uuidv4();304 const value = uuidv4();305 let retries = 0;306 let didRequestRetry = false;307 queue.setRetryJobDelay('echo', async (attempt, error) => {308 await new Promise((resolve) => setTimeout(resolve, 10));309 if (!didRequestRetry) {310 expect(attempt).toEqual(1);311 expect(error).toBeInstanceOf(Error);312 didRequestRetry = true;313 return 0;314 }315 expect(attempt).toEqual(2);316 expect(error).toBeInstanceOf(Error);317 return false;318 });319 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);320 const handleRetry = ({ id: retryId }) => {321 if (retryId === id) {322 retries += 1;323 }324 };325 queue.addListener('retry', handleRetry);326 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });327 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });328 expect(retries).toEqual(1);329 queue.removeListener('retry', handleRetry);330 queue.removeRetryJobDelay('echo');331 });332 it('Enqueues to the database and is cleaned up after a fatal error without retrying', async () => {333 const queueId = uuidv4();334 const value = uuidv4();335 let retries = 0;336 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_FATAL_ERROR, value]);337 const handleRetry = ({ id: retryId }) => {338 if (retryId === id) {339 retries += 1;340 }341 };342 queue.addListener('retry', handleRetry);343 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });344 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });345 expect(retries).toEqual(0);346 queue.removeListener('retry', handleRetry);347 });348 it('Cleans up jobs in the reverse order that they were added', async () => {349 const queueId = uuidv4();350 const expectedCleanupValues = [];351 for (let i = 0; i < 10; i += 1) {352 const value = uuidv4();353 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, value]);354 expectedCleanupValues.push(value);355 }356 await queue.onIdle();357 queue.abortQueue(queueId);358 while (expectedCleanupValues.length > 0) {359 const value = expectedCleanupValues.pop();360 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });361 }362 });363 it('Cleans up high priority jobs in the reverse order that they were added', async () => {364 const queueId = uuidv4();365 const expectedCleanupValues = [];366 for (let i = 0; i < 10; i += 1) {367 const value = uuidv4();368 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, value], { prioritize: true });369 expectedCleanupValues.push(value);370 }371 await queue.onIdle();372 queue.abortQueue(queueId);373 while (expectedCleanupValues.length > 0) {374 const value = expectedCleanupValues.pop();375 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });376 }377 });378 it('Cleans up jobs in the reverse order that they were added following a fatal error', async () => {379 const queueId = uuidv4();380 const valueA = uuidv4();381 const valueB = uuidv4();382 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);383 await enqueueToDatabase(queueId, 'echo', [TRIGGER_FATAL_ERROR, valueB]);384 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueB, cleanupData: { value: valueB } });385 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueA, cleanupData: { value: valueA } });386 });387 it('Enqueues to the database and is cleaned up after an error, retrying once after a 100ms delay if the retry delay function returns 100', async () => {388 const queueId = uuidv4();389 const value = uuidv4();390 let retries = 0;391 let didRequestRetry = false;392 queue.setRetryJobDelay('echo', (attempt, error) => {393 if (!didRequestRetry) {394 expect(attempt).toEqual(1);395 expect(error).toBeInstanceOf(Error);396 didRequestRetry = true;397 return 100;398 }399 expect(attempt).toEqual(2);400 expect(error).toBeInstanceOf(Error);401 return false;402 });403 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR, value]);404 const handleRetry = ({ id: retryId }) => {405 if (retryId === id) {406 retries += 1;407 }408 };409 queue.addListener('retry', handleRetry);410 await expectAsync(queue).toEmit('retryDelay', { id, queueId, retryDelay: 100 });411 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value, cleanupData: { value } });412 await expectAsync(queue).toEmit('fatalError', { queueId, id, error: jasmine.any(Error) });413 expect(retries).toEqual(1);414 queue.removeListener('retry', handleRetry);415 queue.removeRetryJobDelay('echo');416 });417 it('Removes completed items older than a certain age', async () => {418 const queueId = uuidv4();419 const value = uuidv4();420 const args = [TRIGGER_NO_ERROR, value];421 await enqueueToDatabase(queueId, 'echo', args);422 await expectAsync(getCompletedJobsCountFromDatabase(queueId)).toBeResolvedTo(0);423 await queue.onIdle();424 await expectAsync(getCompletedJobsCountFromDatabase(queueId)).toBeResolvedTo(1);425 await removeCompletedExpiredItemsFromDatabase(60 * 1000);426 await expectAsync(getCompletedJobsCountFromDatabase(queueId)).toBeResolvedTo(1);427 await removeCompletedExpiredItemsFromDatabase(0);428 await expectAsync(getCompletedJobsCountFromDatabase(queueId)).toBeResolvedTo(0);429 });430 it('Delays execution of items', async () => {431 const queueId = uuidv4();432 const value = uuidv4();433 let echoReceivedTime = -1;434 const start = Date.now();435 await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, value], { delay: 250 });436 const handleEcho = ({ value: echoValue }) => {437 if (echoValue === value) {438 echoReceivedTime = Date.now();439 }440 };441 echoEmitter.addListener('echo', handleEcho);442 await queue.onIdle();443 echoEmitter.removeListener('echo', handleEcho);444 expect(echoReceivedTime).toBeGreaterThan(start + 250);445 });446 it('Enqueues to the database and is cleaned up after an error following a defined delay', async () => {447 const queueId = uuidv4();448 const value = uuidv4();449 const args = [TRIGGER_NO_ERROR, value];450 const id = await enqueueToDatabase(queueId, 'echo', args);451 await queue.onIdle();452 const start = Date.now();453 await markCleanupStartAfterInDatabase(id, Date.now() + 250);454 let echoCleanupReceivedTime = -1;455 const handleEchoCleanup = ({ value: echoValue }) => {456 if (echoValue === value) {457 echoCleanupReceivedTime = Date.now();458 }459 };460 echoEmitter.addListener('echoCleanupComplete', handleEchoCleanup);461 await queue.abortQueue(queueId);462 await queue.onIdle();463 echoEmitter.removeListener('echoCleanupComplete', handleEchoCleanup);464 expect(echoCleanupReceivedTime).toBeGreaterThan(start + 250);465 });466 it('Enqueues to the database and stops cleanup after FatalError', async () => {467 const queueId = uuidv4();468 const value = uuidv4();469 const args = [TRIGGER_FATAL_ERROR_IN_CLEANUP, value];470 const id = await enqueueToDatabase(queueId, 'echo', args);471 await queue.onIdle();472 await queue.abortQueue(queueId);473 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });474 });475 it('Enqueues to the database and stops cleanup after Error', async () => {476 const queueId = uuidv4();477 const value = uuidv4();478 const args = [TRIGGER_ERROR_IN_CLEANUP, value];479 const id = await enqueueToDatabase(queueId, 'echo', args);480 await queue.onIdle();481 await queue.abortQueue(queueId);482 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });483 });484 it('Does not retry cleanup if a retry cleanup delay function returns false', async () => {485 const queueId = uuidv4();486 const value = uuidv4();487 let cleanupAttempts = 0;488 queue.setRetryCleanupDelay('echo', (attempt, error) => {489 expect(attempt).toEqual(1);490 expect(error).toBeInstanceOf(Error);491 return false;492 });493 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR_IN_CLEANUP, value]);494 await queue.onIdle();495 const handleCleanupStart = ({ id: cleanupId }) => {496 if (cleanupId === id) {497 cleanupAttempts += 1;498 }499 };500 queue.addListener('cleanupStart', handleCleanupStart);501 await queue.abortQueue(queueId);502 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });503 expect(cleanupAttempts).toEqual(1);504 queue.removeListener('cleanupStart', handleCleanupStart);505 queue.removeRetryCleanupDelay('echo');506 });507 it('Does not retry cleanup if a retry cleanup delay function throws an error', async () => {508 const queueId = uuidv4();509 const value = uuidv4();510 let cleanupAttempts = 0;511 queue.setRetryCleanupDelay('echo', (attempt, error) => {512 expect(attempt).toEqual(1);513 expect(error).toBeInstanceOf(Error);514 throw new Error('RetryCleanupDelay error');515 });516 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR_IN_CLEANUP, value]);517 await queue.onIdle();518 const handleCleanupStart = ({ id: cleanupId }) => {519 if (cleanupId === id) {520 cleanupAttempts += 1;521 }522 };523 queue.addListener('cleanupStart', handleCleanupStart);524 await queue.abortQueue(queueId);525 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });526 expect(cleanupAttempts).toEqual(1);527 queue.removeListener('cleanupStart', handleCleanupStart);528 queue.removeRetryCleanupDelay('echo');529 });530 it('Retries cleanup if a retry cleanup delay function returns 0', async () => {531 const queueId = uuidv4();532 const value = uuidv4();533 let cleanupAttempts = 0;534 let didRequestRetry = false;535 queue.setRetryCleanupDelay('echo', (attempt, error) => {536 if (!didRequestRetry) {537 didRequestRetry = true;538 expect(attempt).toEqual(1);539 expect(error).toBeInstanceOf(Error);540 return 0;541 }542 expect(attempt).toEqual(2);543 expect(error).toBeInstanceOf(Error);544 return false;545 });546 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR_IN_CLEANUP, value]);547 await queue.onIdle();548 const handleCleanupStart = ({ id: cleanupId }) => {549 if (cleanupId === id) {550 cleanupAttempts += 1;551 }552 };553 queue.addListener('cleanupStart', handleCleanupStart);554 await queue.abortQueue(queueId);555 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });556 expect(cleanupAttempts).toEqual(2);557 queue.removeListener('cleanupStart', handleCleanupStart);558 queue.removeRetryCleanupDelay('echo');559 });560 it('Does not retry cleanup if an asynchronous retry cleanup delay function returns false', async () => {561 const queueId = uuidv4();562 const value = uuidv4();563 let cleanupAttempts = 0;564 queue.setRetryCleanupDelay('echo', async (attempt, error) => {565 await new Promise((resolve) => setTimeout(resolve, 50));566 expect(attempt).toEqual(1);567 expect(error).toBeInstanceOf(Error);568 return false;569 });570 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR_IN_CLEANUP, value]);571 await queue.onIdle();572 const handleCleanupStart = ({ id: cleanupId }) => {573 if (cleanupId === id) {574 cleanupAttempts += 1;575 }576 };577 queue.addListener('cleanupStart', handleCleanupStart);578 await queue.abortQueue(queueId);579 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });580 expect(cleanupAttempts).toEqual(1);581 queue.removeListener('cleanupStart', handleCleanupStart);582 queue.removeRetryCleanupDelay('echo');583 });584 it('Does not retry cleanup if an asynchronous retry cleanup delay function throws an error', async () => {585 const queueId = uuidv4();586 const value = uuidv4();587 let cleanupAttempts = 0;588 queue.setRetryCleanupDelay('echo', async (attempt, error) => {589 await new Promise((resolve) => setTimeout(resolve, 50));590 expect(attempt).toEqual(1);591 expect(error).toBeInstanceOf(Error);592 throw new Error('RetryCleanupDelay error');593 });594 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR_IN_CLEANUP, value]);595 await queue.onIdle();596 const handleCleanupStart = ({ id: cleanupId }) => {597 if (cleanupId === id) {598 cleanupAttempts += 1;599 }600 };601 queue.addListener('cleanupStart', handleCleanupStart);602 await queue.abortQueue(queueId);603 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });604 expect(cleanupAttempts).toEqual(1);605 queue.removeListener('cleanupStart', handleCleanupStart);606 queue.removeRetryCleanupDelay('echo');607 });608 it('Retries cleanup if an asynchronous retry cleanup delay function returns 0', async () => {609 const queueId = uuidv4();610 const value = uuidv4();611 let cleanupAttempts = 0;612 let didRequestRetry = false;613 queue.setRetryCleanupDelay('echo', async (attempt, error) => {614 await new Promise((resolve) => setTimeout(resolve, 50));615 if (!didRequestRetry) {616 didRequestRetry = true;617 expect(attempt).toEqual(1);618 expect(error).toBeInstanceOf(Error);619 return 0;620 }621 expect(attempt).toEqual(2);622 expect(error).toBeInstanceOf(Error);623 return false;624 });625 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR_IN_CLEANUP, value]);626 await queue.onIdle();627 const handleCleanupStart = ({ id: cleanupId }) => {628 if (cleanupId === id) {629 cleanupAttempts += 1;630 }631 };632 queue.addListener('cleanupStart', handleCleanupStart);633 await queue.abortQueue(queueId);634 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });635 expect(cleanupAttempts).toEqual(2);636 queue.removeListener('cleanupStart', handleCleanupStart);637 queue.removeRetryCleanupDelay('echo');638 });639 it('Retries cleanup after 100ms if an asynchronous retry cleanup delay function returns 100', async () => {640 const queueId = uuidv4();641 const value = uuidv4();642 let cleanupAttempts = 0;643 let didRequestRetry = false;644 queue.setRetryCleanupDelay('echo', async (attempt, error) => {645 await new Promise((resolve) => setTimeout(resolve, 50));646 if (!didRequestRetry) {647 didRequestRetry = true;648 expect(attempt).toEqual(1);649 expect(error).toBeInstanceOf(Error);650 return 100;651 }652 expect(attempt).toEqual(2);653 expect(error).toBeInstanceOf(Error);654 return false;655 });656 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_ERROR_IN_CLEANUP, value]);657 await queue.onIdle();658 const handleCleanupStart = ({ id: cleanupId }) => {659 if (cleanupId === id) {660 cleanupAttempts += 1;661 }662 };663 queue.addListener('cleanupStart', handleCleanupStart);664 await queue.abortQueue(queueId);665 await expectAsync(queue).toEmit('retryCleanupDelay', { id, queueId, retryCleanupDelay: 100 });666 await expectAsync(queue).toEmit('fatalCleanupError', { id, queueId });667 expect(cleanupAttempts).toEqual(2);668 queue.removeListener('cleanupStart', handleCleanupStart);669 queue.removeRetryCleanupDelay('echo');670 });671 it('Runs cleanup on a long-running job if it was aborted', async () => {672 const queueId = uuidv4();673 const type = uuidv4();674 let didRunHandler = false;675 let didRunCleanup = false;676 let didRunRetryJobDelay = false;677 const handler = async () => {678 await queue.abortQueue(queueId);679 await new Promise((resolve) => setTimeout(resolve, 100));680 didRunHandler = true;681 };682 const cleanup = async () => {683 didRunCleanup = true;684 };685 const retryJobDelay = () => {686 didRunRetryJobDelay = true;687 return 0;688 };689 queue.setHandler(type, handler);690 queue.setCleanup(type, cleanup);691 queue.setRetryJobDelay(type, retryJobDelay);692 await enqueueToDatabase(queueId, type, []);693 await queue.onIdle();694 queue.removeHandler(type);695 queue.removeCleanup(type);696 queue.removeRetryJobDelay(type);697 expect(didRunHandler).toEqual(true);698 expect(didRunCleanup).toEqual(true);699 expect(didRunRetryJobDelay).toEqual(false);700 });701 it('Runs cleanup on a long-running job that throws an error if it was aborted', async () => {702 const queueId = uuidv4();703 const type = uuidv4();704 let didRunHandler = false;705 let didRunCleanup = false;706 let didRunRetryJobDelay = false;707 const handler = async () => {708 await queue.abortQueue(queueId);709 await new Promise((resolve) => setTimeout(resolve, 100));710 didRunHandler = true;711 throw new Error('Test error in aborted queue');712 };713 const cleanup = async () => {714 didRunCleanup = true;715 };716 const retryJobDelay = () => {717 didRunRetryJobDelay = true;718 return 0;719 };720 queue.setHandler(type, handler);721 queue.setCleanup(type, cleanup);722 queue.setRetryJobDelay(type, retryJobDelay);723 await enqueueToDatabase(queueId, type, []);724 await queue.onIdle();725 queue.removeHandler(type);726 queue.removeCleanup(type);727 queue.removeRetryJobDelay(type);728 expect(didRunHandler).toEqual(true);729 expect(didRunCleanup).toEqual(true);730 expect(didRunRetryJobDelay).toEqual(false);731 });732 it('Prevents running a scheduled job that was aborted', async () => {733 const queueId = uuidv4();734 const type = uuidv4();735 let handlerCount = 0;736 let cleanupCount = 0;737 let retryCount = 0;738 const handler = async (args:Array<any>) => {739 const [shouldAbortQueue] = args;740 await new Promise((resolve) => setTimeout(resolve, 100));741 if (shouldAbortQueue) {742 await queue.abortQueue(queueId);743 }744 handlerCount += 1;745 };746 const cleanup = async () => {747 cleanupCount += 1;748 };749 const retryJobDelay = () => {750 retryCount += 1;751 return 0;752 };753 queue.setHandler(type, handler);754 queue.setCleanup(type, cleanup);755 queue.setRetryJobDelay(type, retryJobDelay);756 await enqueueToDatabase(queueId, type, [true]);757 await enqueueToDatabase(queueId, type, [false], { delay: 1000 });758 await queue.onIdle();759 queue.removeHandler(type);760 queue.removeCleanup(type);761 queue.removeRetryJobDelay(type);762 expect(handlerCount).toEqual(1);763 expect(cleanupCount).toEqual(1);764 expect(retryCount).toEqual(0);765 });766 it('Emits queueActive and queueInactive events when a queue becomes active or inactive', async () => {767 const queueId = uuidv4();768 const value = uuidv4();769 const id = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, value]);770 await expectAsync(queue).toEmit('queueActive', queueId);771 await expectAsync(jobEmitter).toEmit('jobUpdate', id, queueId, 'echo', JOB_COMPLETE_STATUS);772 queue.clear();773 await expectAsync(queue).toEmit('queueInactive', queueId); // Triggers after 5s or on a 'clearing' event774 });775 it('Cleans up and removes a job from the queue if a job is marked with status "clean up and remove" after a job is complete', async () => {776 const queueId = uuidv4();777 const valueA = uuidv4();778 const valueB = uuidv4();779 const idA = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);780 const idB = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueB]);781 await expectAsync(echoEmitter).toEmit('echo', { value: valueA });782 await expectAsync(echoEmitter).toEmit('echo', { value: valueB });783 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{784 id: idA,785 queueId,786 type: 'echo',787 args: [TRIGGER_NO_ERROR, valueA],788 attempt: 0,789 created: jasmine.any(Number),790 status: JOB_COMPLETE_STATUS,791 startAfter: jasmine.any(Number),792 prioritize: false,793 }, {794 id: idB,795 queueId,796 type: 'echo',797 args: [TRIGGER_NO_ERROR, valueB],798 attempt: 0,799 created: jasmine.any(Number),800 status: JOB_COMPLETE_STATUS,801 startAfter: jasmine.any(Number),802 prioritize: false,803 }]);804 await markJobCleanupAndRemoveInDatabase(idA);805 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueA, cleanupData: { value: valueA } });806 await queue.onIdle();807 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{808 id: idB,809 queueId,810 type: 'echo',811 args: [TRIGGER_NO_ERROR, valueB],812 attempt: 0,813 created: jasmine.any(Number),814 status: JOB_COMPLETE_STATUS,815 startAfter: jasmine.any(Number),816 prioritize: false,817 }]);818 });819 it('Imports jobs into the database', async () => {820 const queueId = uuidv4();821 const valueA = uuidv4();822 const valueB = uuidv4();823 const idA = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);824 const idB = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueB]);825 await queue.onIdle();826 const jobs1 = await getJobsInQueueFromDatabase(queueId);827 const cleanups1 = await getCleanupsInQueueFromDatabase(queueId);828 expect(jobs1).toEqual([{829 id: idA,830 queueId,831 type: 'echo',832 args: [TRIGGER_NO_ERROR, valueA],833 attempt: 0,834 created: jasmine.any(Number),835 status: JOB_COMPLETE_STATUS,836 startAfter: jasmine.any(Number),837 prioritize: false,838 }, {839 id: idB,840 queueId,841 type: 'echo',842 args: [TRIGGER_NO_ERROR, valueB],843 attempt: 0,844 created: jasmine.any(Number),845 status: JOB_COMPLETE_STATUS,846 startAfter: jasmine.any(Number),847 prioritize: false,848 }]);849 expect(cleanups1).toEqual([{850 id: idA,851 queueId,852 attempt: 0,853 data: { value: valueA },854 startAfter: jasmine.any(Number),855 }, {856 id: idB,857 queueId,858 attempt: 0,859 data: { value: valueB },860 startAfter: jasmine.any(Number),861 }]);862 await queue.clear();863 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);864 await expectAsync(getCleanupsInQueueFromDatabase(queueId)).toBeResolvedTo([]);865 const newJobs = await importJobsAndCleanups(jobs1, cleanups1);866 const jobs2 = await getJobsInQueueFromDatabase(queueId);867 const cleanups2 = await getCleanupsInQueueFromDatabase(queueId);868 expect(newJobs).toEqual(jobs2);869 expect(jobs2).toEqual([{870 id: jasmine.any(Number),871 queueId,872 type: 'echo',873 args: [TRIGGER_NO_ERROR, valueA],874 attempt: 0,875 created: jasmine.any(Number),876 status: JOB_COMPLETE_STATUS,877 startAfter: jasmine.any(Number),878 prioritize: false,879 }, {880 id: jasmine.any(Number),881 queueId,882 type: 'echo',883 args: [TRIGGER_NO_ERROR, valueB],884 attempt: 0,885 created: jasmine.any(Number),886 status: JOB_COMPLETE_STATUS,887 startAfter: jasmine.any(Number),888 prioritize: false,889 }]);890 expect(cleanups2).toEqual([{891 id: jobs2[0].id,892 queueId,893 attempt: 0,894 data: { value: valueA },895 startAfter: jasmine.any(Number),896 }, {897 id: jobs2[1].id,898 queueId,899 attempt: 0,900 data: { value: valueB },901 startAfter: jasmine.any(Number),902 }]);903 });904 it('Cleans up and removes jobs from the queue if abortAndRemove is called', async () => {905 const queueId = uuidv4();906 const valueA = uuidv4();907 const valueB = uuidv4();908 const idA = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);909 const idB = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueB]);910 await expectAsync(echoEmitter).toEmit('echo', { value: valueA });911 await expectAsync(echoEmitter).toEmit('echo', { value: valueB });912 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{913 id: idA,914 queueId,915 type: 'echo',916 args: [TRIGGER_NO_ERROR, valueA],917 attempt: 0,918 created: jasmine.any(Number),919 status: JOB_COMPLETE_STATUS,920 startAfter: jasmine.any(Number),921 prioritize: false,922 }, {923 id: idB,924 queueId,925 type: 'echo',926 args: [TRIGGER_NO_ERROR, valueB],927 attempt: 0,928 created: jasmine.any(Number),929 status: JOB_COMPLETE_STATUS,930 startAfter: jasmine.any(Number),931 prioritize: false,932 }]);933 queue.abortAndRemoveQueue(queueId);934 await expectAsync(queue).toEmit('abortAndRemoveQueue', queueId);935 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueB, cleanupData: { value: valueB } });936 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueA, cleanupData: { value: valueA } });937 await queue.onIdle();938 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);939 });940 it('Cleans up and removes jobs from the queue with job IDs larger than a specified number if abortAndRemoveQueueJobsGreaterThanId is called', async () => {941 await queue.disableStartOnJob();942 const queueId = uuidv4();943 const valueA = uuidv4();944 const valueB = uuidv4();945 const valueC = uuidv4();946 const idA = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);947 const idB = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueB]);948 const idC = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueC]);949 await queue.dequeue();950 await expectAsync(echoEmitter).toEmit('echo', { value: valueA });951 await expectAsync(echoEmitter).toEmit('echo', { value: valueB });952 await expectAsync(echoEmitter).toEmit('echo', { value: valueC });953 await queue.onIdle();954 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{955 id: idA,956 queueId,957 type: 'echo',958 args: [TRIGGER_NO_ERROR, valueA],959 attempt: 0,960 created: jasmine.any(Number),961 status: JOB_COMPLETE_STATUS,962 startAfter: jasmine.any(Number),963 prioritize: false,964 }, {965 id: idB,966 queueId,967 type: 'echo',968 args: [TRIGGER_NO_ERROR, valueB],969 attempt: 0,970 created: jasmine.any(Number),971 status: JOB_COMPLETE_STATUS,972 startAfter: jasmine.any(Number),973 prioritize: false,974 }, {975 id: idC,976 queueId,977 type: 'echo',978 args: [TRIGGER_NO_ERROR, valueC],979 attempt: 0,980 created: jasmine.any(Number),981 status: JOB_COMPLETE_STATUS,982 startAfter: jasmine.any(Number),983 prioritize: false,984 }]);985 queue.abortAndRemoveQueueJobsGreaterThanId(queueId, idA);986 await expectAsync(queue).toEmit('abortAndRemoveQueueJobs', queueId, idA);987 queue.dequeue();988 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueC, cleanupData: { value: valueC } });989 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueB, cleanupData: { value: valueB } });990 await queue.onIdle();991 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{992 id: idA,993 queueId,994 type: 'echo',995 args: [TRIGGER_NO_ERROR, valueA],996 attempt: 0,997 created: jasmine.any(Number),998 status: JOB_COMPLETE_STATUS,999 startAfter: jasmine.any(Number),1000 prioritize: false,1001 }]);1002 });1003 it('Cleans up and removes a job from the queue if a job is marked with status "clean up and remove" after a job is complete if jobEmitter is not active', async () => {1004 queue.disableStartOnJob();1005 const queueId = uuidv4();1006 const valueA = uuidv4();1007 const valueB = uuidv4();1008 const idA = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueA]);1009 const idB = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, valueB]);1010 queue.dequeue();1011 await expectAsync(echoEmitter).toEmit('echo', { value: valueA });1012 await expectAsync(echoEmitter).toEmit('echo', { value: valueB });1013 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1014 id: idA,1015 queueId,1016 type: 'echo',1017 args: [TRIGGER_NO_ERROR, valueA],1018 attempt: 0,1019 created: jasmine.any(Number),1020 status: JOB_COMPLETE_STATUS,1021 startAfter: jasmine.any(Number),1022 prioritize: false,1023 }, {1024 id: idB,1025 queueId,1026 type: 'echo',1027 args: [TRIGGER_NO_ERROR, valueB],1028 attempt: 0,1029 created: jasmine.any(Number),1030 status: JOB_COMPLETE_STATUS,1031 startAfter: jasmine.any(Number),1032 prioritize: false,1033 }]);1034 await queue.onIdle();1035 await markJobCleanupAndRemoveInDatabase(idA);1036 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1037 id: idA,1038 queueId,1039 type: 'echo',1040 args: [TRIGGER_NO_ERROR, valueA],1041 attempt: 0,1042 created: jasmine.any(Number),1043 status: JOB_CLEANUP_AND_REMOVE_STATUS,1044 startAfter: jasmine.any(Number),1045 prioritize: false,1046 }, {1047 id: idB,1048 queueId,1049 type: 'echo',1050 args: [TRIGGER_NO_ERROR, valueB],1051 attempt: 0,1052 created: jasmine.any(Number),1053 status: JOB_COMPLETE_STATUS,1054 startAfter: jasmine.any(Number),1055 prioritize: false,1056 }]);1057 queue.dequeue();1058 await expectAsync(echoEmitter).toEmit('echoCleanupComplete', { value: valueA, cleanupData: { value: valueA } });1059 await queue.onIdle();1060 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1061 id: idB,1062 queueId,1063 type: 'echo',1064 args: [TRIGGER_NO_ERROR, valueB],1065 attempt: 0,1066 created: jasmine.any(Number),1067 status: JOB_COMPLETE_STATUS,1068 startAfter: jasmine.any(Number),1069 prioritize: false,1070 }]);1071 queue.enableStartOnJob();1072 });1073 it('Cleans up and removes a job from the queue if a job is marked with status "clean up and remove" while a job is in progress', async () => {1074 let id;1075 let didRunCleanup = false;1076 const handler = async () => {1077 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1078 id,1079 queueId,1080 type,1081 args: [],1082 attempt: 0,1083 created: jasmine.any(Number),1084 status: JOB_ERROR_STATUS,1085 startAfter: jasmine.any(Number),1086 prioritize: false,1087 }]);1088 if (typeof id === 'number') {1089 await markJobCleanupAndRemoveInDatabase(id);1090 }1091 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1092 id,1093 queueId,1094 type,1095 args: [],1096 attempt: 0,1097 created: jasmine.any(Number),1098 status: JOB_CLEANUP_AND_REMOVE_STATUS,1099 startAfter: jasmine.any(Number),1100 prioritize: false,1101 }]);1102 };1103 const cleanup = async () => {1104 didRunCleanup = true;1105 };1106 const type = uuidv4();1107 queue.setHandler(type, handler);1108 queue.setCleanup(type, cleanup);1109 const queueId = uuidv4();1110 id = await enqueueToDatabase(queueId, type, []);1111 await queue.onIdle();1112 expect(didRunCleanup).toEqual(true);1113 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1114 queue.removeHandler(type);1115 queue.removeCleanup(type);1116 });1117 it('Removes a job marked as "cleanup and remove" while the cleanup handler is running', async () => {1118 let id;1119 let didRunCleanup = false;1120 const handler = async () => {1121 throw new FatalError('Test fatal error');1122 };1123 const cleanup = async () => {1124 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1125 id,1126 queueId,1127 type,1128 args: [],1129 attempt: 1,1130 created: jasmine.any(Number),1131 status: JOB_CLEANUP_STATUS,1132 startAfter: jasmine.any(Number),1133 prioritize: false,1134 }]);1135 if (typeof id === 'number') {1136 didRunCleanup = true;1137 await markJobCleanupAndRemoveInDatabase(id);1138 }1139 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1140 id,1141 queueId,1142 type,1143 args: [],1144 attempt: 1,1145 created: jasmine.any(Number),1146 status: JOB_CLEANUP_AND_REMOVE_STATUS,1147 startAfter: jasmine.any(Number),1148 prioritize: false,1149 }]);1150 };1151 const type = uuidv4();1152 queue.setHandler(type, handler);1153 queue.setCleanup(type, cleanup);1154 const queueId = uuidv4();1155 id = await enqueueToDatabase(queueId, type, []);1156 await queue.onIdle();1157 expect(didRunCleanup).toEqual(true);1158 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1159 queue.removeHandler(type);1160 queue.removeCleanup(type);1161 });1162 it('Removes a job marked as "cleanup and remove" while the non-fatal error handler is running', async () => {1163 let id;1164 let handlerCount = 0;1165 let didRunCleanup = false;1166 const type = uuidv4();1167 const retryJobDelay = (attempt, error) => {1168 if (didRunCleanup) {1169 return false;1170 }1171 expect(handlerCount).toEqual(1);1172 expect(error).toBeInstanceOf(Error);1173 return 0;1174 };1175 const handler = async () => {1176 handlerCount += 1;1177 throw new Error('Test non-fatal error');1178 };1179 const cleanup = async () => {1180 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1181 id,1182 queueId,1183 type,1184 args: [],1185 attempt: 1,1186 created: jasmine.any(Number),1187 status: JOB_ERROR_STATUS,1188 startAfter: jasmine.any(Number),1189 prioritize: false,1190 }]);1191 if (typeof id === 'number') {1192 didRunCleanup = true;1193 await markJobCleanupAndRemoveInDatabase(id);1194 }1195 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1196 id,1197 queueId,1198 type,1199 args: [],1200 attempt: 1,1201 created: jasmine.any(Number),1202 status: JOB_CLEANUP_AND_REMOVE_STATUS,1203 startAfter: jasmine.any(Number),1204 prioritize: false,1205 }]);1206 };1207 queue.setHandler(type, handler);1208 queue.setCleanup(type, cleanup);1209 queue.setRetryJobDelay(type, retryJobDelay);1210 const queueId = uuidv4();1211 id = await enqueueToDatabase(queueId, type, []);1212 await queue.onIdle();1213 expect(handlerCount).toEqual(1);1214 expect(didRunCleanup).toEqual(true);1215 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1216 queue.removeHandler(type);1217 queue.removeCleanup(type);1218 queue.removeRetryJobDelay(type);1219 });1220 it('Removes a job marked as "cleanup and remove" before the handler starts', async () => {1221 let handlerCount = 0;1222 let cleanupCount = 0;1223 let idB;1224 const type = uuidv4();1225 const handler = async () => {1226 if (handlerCount === 0) {1227 while (typeof idB !== 'number') {1228 await new Promise((resolve) => setTimeout(resolve, 20));1229 }1230 await markJobCleanupAndRemoveInDatabase(idB);1231 }1232 handlerCount += 1;1233 };1234 const cleanup = async () => {1235 cleanupCount += 1;1236 };1237 queue.setHandler(type, handler);1238 queue.setCleanup(type, cleanup);1239 const queueId = uuidv4();1240 const idA = await enqueueToDatabase(queueId, type, []);1241 idB = await enqueueToDatabase(queueId, type, []);1242 await queue.onIdle();1243 expect(handlerCount).toEqual(1);1244 expect(cleanupCount).toEqual(0);1245 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1246 id: idA,1247 queueId,1248 type,1249 args: [],1250 attempt: 0,1251 created: jasmine.any(Number),1252 status: JOB_COMPLETE_STATUS,1253 startAfter: jasmine.any(Number),1254 prioritize: false,1255 }]);1256 queue.removeHandler(type);1257 queue.removeCleanup(type);1258 });1259 it('Removes a job marked as "cleanup and remove" after the job is started but before the handler runs', async () => {1260 let handlerCount = 0;1261 let cleanupCount = 0;1262 const type = uuidv4();1263 const handler = async () => {1264 handlerCount += 1;1265 };1266 const cleanup = async () => {1267 cleanupCount += 1;1268 };1269 queue.setHandler(type, handler);1270 queue.setCleanup(type, cleanup);1271 const queueId = uuidv4();1272 const id = await enqueueToDatabase(queueId, type, []);1273 await markJobCleanupAndRemoveInDatabase(id);1274 await queue.onIdle();1275 expect(handlerCount).toEqual(0);1276 expect(cleanupCount).toEqual(0);1277 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1278 queue.removeHandler(type);1279 queue.removeCleanup(type);1280 });1281 it('Cleanly removes a job removed without emitting jobDelete events while the handler runs', async () => {1282 let handlerCount = 0;1283 let cleanupCount = 0;1284 let id;1285 const type = uuidv4();1286 const handler = async () => {1287 if (typeof id === 'number') {1288 await silentlyRemoveJobFromDatabase(id);1289 }1290 handlerCount += 1;1291 };1292 const cleanup = async () => {1293 cleanupCount += 1;1294 };1295 queue.setHandler(type, handler);1296 queue.setCleanup(type, cleanup);1297 const queueId = uuidv4();1298 id = await enqueueToDatabase(queueId, type, []);1299 await queue.onIdle();1300 expect(handlerCount).toEqual(1);1301 expect(cleanupCount).toEqual(1);1302 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1303 queue.removeHandler(type);1304 queue.removeCleanup(type);1305 });1306 it('Cleanly removes all jobs in a queue without emitting jobDelete events while the handler runs', async () => {1307 let handlerCount = 0;1308 let cleanupCount = 0;1309 let id;1310 const type = uuidv4();1311 const handler = async () => {1312 if (typeof id === 'number') {1313 await silentlyRemoveQueueFromDatabase(queueId);1314 }1315 handlerCount += 1;1316 };1317 const cleanup = async () => {1318 cleanupCount += 1;1319 };1320 queue.setHandler(type, handler);1321 queue.setCleanup(type, cleanup);1322 const queueId = uuidv4();1323 id = await enqueueToDatabase(queueId, type, []);1324 await queue.onIdle();1325 expect(handlerCount).toEqual(1);1326 expect(cleanupCount).toEqual(1);1327 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1328 queue.removeHandler(type);1329 queue.removeCleanup(type);1330 });1331 it('Cleanly removes a job removed while the handler runs', async () => {1332 let handlerCount = 0;1333 let cleanupCount = 0;1334 let id;1335 const type = uuidv4();1336 const handler = async () => {1337 if (typeof id === 'number') {1338 await removeJobFromDatabase(id);1339 }1340 handlerCount += 1;1341 };1342 const cleanup = async () => {1343 cleanupCount += 1;1344 };1345 queue.setHandler(type, handler);1346 queue.setCleanup(type, cleanup);1347 const queueId = uuidv4();1348 id = await enqueueToDatabase(queueId, type, []);1349 await queue.onIdle();1350 expect(handlerCount).toEqual(1);1351 expect(cleanupCount).toEqual(1);1352 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1353 queue.removeHandler(type);1354 queue.removeCleanup(type);1355 });1356 it('Removes a job marked as "cleanup and remove" during a delayed start', async () => {1357 let handlerCount = 0;1358 let cleanupCount = 0;1359 const type = uuidv4();1360 const handler = async () => {1361 handlerCount += 1;1362 };1363 const cleanup = async () => {1364 cleanupCount += 1;1365 };1366 queue.setHandler(type, handler);1367 queue.setCleanup(type, cleanup);1368 const queueId = uuidv4();1369 const id = await enqueueToDatabase(queueId, type, [], { delay: 1000 });1370 await expectAsync(queue).toEmit('dequeue', { id });1371 await markJobCleanupAndRemoveInDatabase(id);1372 await queue.onIdle();1373 expect(handlerCount).toEqual(0);1374 expect(cleanupCount).toEqual(0);1375 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([]);1376 queue.removeHandler(type);1377 queue.removeCleanup(type);1378 });1379 it('Removes a job marked as "cleanup and remove" if the job encounters an error', async () => {1380 const queueId = uuidv4();1381 const type = uuidv4();1382 let handlerCount = 0;1383 let cleanupCount = 0;1384 let id;1385 const handler = async () => {1386 handlerCount += 1;1387 throw new Error('Test error');1388 };1389 const cleanup = async () => {1390 cleanupCount += 1;1391 };1392 const retryJobDelay = () => {1393 setTimeout(() => {1394 markJobCleanupAndRemoveInDatabase(id);1395 }, 100);1396 return 1000;1397 };1398 queue.setHandler(type, handler);1399 queue.setCleanup(type, cleanup);1400 queue.setRetryJobDelay(type, retryJobDelay);1401 id = await enqueueToDatabase(queueId, type, []);1402 await queue.onIdle();1403 queue.removeHandler(type);1404 queue.removeCleanup(type);1405 queue.removeRetryJobDelay(type);1406 expect(handlerCount).toEqual(1);1407 expect(cleanupCount).toEqual(1);1408 });1409 it('Retries a queue', async () => {1410 const queueId = uuidv4();1411 const type = uuidv4();1412 let handlerCount = 0;1413 let cleanupCount = 0;1414 const handler = async () => {1415 handlerCount += 1;1416 if (handlerCount === 1) {1417 throw new Error('Test error');1418 }1419 };1420 const cleanup = async () => {1421 cleanupCount += 1;1422 };1423 queue.setHandler(type, handler);1424 queue.setCleanup(type, cleanup);1425 await enqueueToDatabase(queueId, type, []);1426 await queue.onIdle();1427 await expectAsync(getQueueStatus(queueId)).toBeResolvedTo(QUEUE_ERROR_STATUS);1428 expect(handlerCount).toEqual(1);1429 expect(cleanupCount).toEqual(1);1430 await queue.onIdle();1431 await queue.retryQueue(queueId);1432 await queue.onIdle();1433 await expectAsync(getQueueStatus(queueId)).toBeResolvedTo(QUEUE_COMPLETE_STATUS);1434 expect(handlerCount).toEqual(2);1435 expect(cleanupCount).toEqual(1);1436 queue.removeHandler(type);1437 queue.removeCleanup(type);1438 });1439 it('Does not retry a completed queue', async () => {1440 const queueId = uuidv4();1441 const type = uuidv4();1442 let handlerCount = 0;1443 let cleanupCount = 0;1444 const handler = async () => {1445 handlerCount += 1;1446 };1447 const cleanup = async () => {1448 cleanupCount += 1;1449 };1450 queue.setHandler(type, handler);1451 queue.setCleanup(type, cleanup);1452 await enqueueToDatabase(queueId, type, []);1453 await queue.onIdle();1454 await expectAsync(getQueueStatus(queueId)).toBeResolvedTo(QUEUE_COMPLETE_STATUS);1455 expect(handlerCount).toEqual(1);1456 expect(cleanupCount).toEqual(0);1457 await queue.onIdle();1458 await queue.retryQueue(queueId);1459 await queue.onIdle();1460 await expectAsync(getQueueStatus(queueId)).toBeResolvedTo(QUEUE_COMPLETE_STATUS);1461 expect(handlerCount).toEqual(1);1462 expect(cleanupCount).toEqual(0);1463 queue.removeHandler(type);1464 queue.removeCleanup(type);1465 });1466 it('Waits for queue operations to complete before retrying', async () => {1467 const queueId = uuidv4();1468 const type = uuidv4();1469 let handlerCount = 0;1470 let cleanupCount = 0;1471 const handler = async () => {1472 handlerCount += 1;1473 if (handlerCount === 1) {1474 await queue.retryQueue(queueId);1475 await new Promise((resolve) => setTimeout(resolve, 10));1476 throw new Error('Test error');1477 }1478 };1479 const cleanup = async () => {1480 cleanupCount += 1;1481 };1482 queue.setHandler(type, handler);1483 queue.setCleanup(type, cleanup);1484 await enqueueToDatabase(queueId, type, []);1485 await queue.onIdle();1486 await expectAsync(getQueueStatus(queueId)).toBeResolvedTo(QUEUE_COMPLETE_STATUS);1487 expect(handlerCount).toEqual(2);1488 expect(cleanupCount).toEqual(1);1489 queue.removeHandler(type);1490 queue.removeCleanup(type);1491 });1492 it('Aborts jobs if they are added to a queue containing aborted jobs', async () => {1493 const queueId = uuidv4();1494 const value = uuidv4();1495 const idA = await enqueueToDatabase(queueId, 'echo', [TRIGGER_FATAL_ERROR, value]);1496 await queue.onIdle();1497 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1498 id: idA,1499 queueId,1500 type: 'echo',1501 args: [TRIGGER_FATAL_ERROR, value],1502 attempt: 1,1503 created: jasmine.any(Number),1504 status: JOB_ABORTED_STATUS,1505 startAfter: jasmine.any(Number),1506 prioritize: false,1507 }]);1508 const idB = await enqueueToDatabase(queueId, 'echo', [TRIGGER_NO_ERROR, value]);1509 await queue.onIdle();1510 await expectAsync(getJobsInQueueFromDatabase(queueId)).toBeResolvedTo([{1511 id: idA,1512 queueId,1513 type: 'echo',1514 args: [TRIGGER_FATAL_ERROR, value],1515 attempt: 1,1516 created: jasmine.any(Number),1517 status: JOB_ABORTED_STATUS,1518 startAfter: jasmine.any(Number),1519 prioritize: false,1520 }, {1521 id: idB,1522 queueId,1523 type: 'echo',1524 args: [TRIGGER_NO_ERROR, value],1525 attempt: 0,1526 created: jasmine.any(Number),1527 status: JOB_ABORTED_STATUS,1528 startAfter: jasmine.any(Number),1529 prioritize: false,1530 }]);1531 });...
make-map.js
Source:make-map.js
1'use strict';2let mapmaker = require('./map-printer')3, rmdir = require('node-rmdir')4, events = require('events')5, mime = require('mime')6, path = require('path')7, fs = require('fs')8, tmp = require('tmp')9// , gm = require('gm')10, Q = require('q')11, pdfDocument = require('pdfkit')12, exec = require('child_process').exec13, cachetimeout = 1000 * 60 * 15 // 15 minutes14// cache items for `cachetimeout` so they don't need to be regenerated again within 30 seconds15, cachedfiles = {}16;17function makeMap(url, options, oncomplete){18 // let url = 'http://localhost:3000/responder-maps/' + view;19 // console.log('sending %s to mapmaker', url);20 // cachedfiles[url] = {}21 mapmaker.makeMap(url, options, oncomplete);22}23function mapcomplete(res, data){24 // console.log('mapcomplete:', data);25 // console.log('mapcomplete', 'this', this, 'data', data);26 if(!data)27 return res.send({error : '?'})28 let datacallback = data instanceof Array ? (evt)=>{29 console.log(evt, 'trying to run datacallbacks for multiple items');30 data.forEach(item=>{31 console.log('about to run datacallback for', item.filepath);32 item.datacallback && item.datacallback();33 item.callback && item.callback();34 });35 }36 : data.callback37 ;38 if(data.error)39 return res.send({error: '-?-'})40 // console.log('this is:', this);41 // console.log('data is:', data);42 // if(data.initOptions){43 console.log('storing cached item for:', this.view)44 let savedItem;45 if(!cachedfiles[this.view]){46 let storeItem = {};47 if(data instanceof Array){48 data.forEach( item=> storeItem[item.type] = {filepath: item.filepath, type:item.type, name: item.sendname, deletetimer: item.deletetimer} )49 }else50 storeItem = {filepath: data.filepath, type:item.type, name: data.sendname, deletetimer: data.deletetimer}51 52 savedItem = cachedfiles[this.view] = storeItem;53 }54 // let sendItem = data instanceof Array ? 55 // data.filter(item=>item.type==this.filetype)[0]56 // : data57 // ;58 let sendItem = savedItem[this.filetype] || savedItem;59 // console.log('sendItem is:');60 // console.dir(sendItem);61 let filepath = sendItem.filepath62 , sendname = sendItem.name63 ;64 // console.log('sendItem:', sendItem);65 // console.log('filepath:', filepath);66 let cleanupattempts = 0;67 function cleanup(evt){68 cleanupattempts+=1;69 console.log('cleaning up - checking if %s exists', filepath) 70 fs.exists(filepath, exists=>{71 if(exists){72 // remove from the memory cache73 delete cachedfiles[this.view]74 75 try{76 datacallback()77 }78 catch(err){79 console.err(err);80 if(cleanupattempts>15)81 return console.warn('too many attempts to cleanup %s. Moving on...', filepath)82 return cleanup(evt);83 }84 }85 })86 }87 try{88 if(datacallback instanceof Function)89 // catch all possible res close events and do temp file cleanup90 res.on('finish', evt=>91 setTimeout(t=>{92 cleanup.call('finish');93 }, cachetimeout)94 )95 .on('close', evt=>96 setTimeout(t=>{97 cleanup.call('close');98 }, cachetimeout)99 )100 .on('error', evt=>101 setTimeout(t=>{102 cleanup.call('error');103 }, cachetimeout)104 )105 return streamFile(filepath, sendname, res)106 }catch(err){107 console.log('error in mapcomplete:', err);108 setTimeout(t=>{109 datacallback instanceof Function && datacallback()110 }, cachetimeout)111 }112 113}114function streamFile(filepath, sendname, res){115 let mimetype = mime.lookup(filepath);116 console.log('streaming:', path.resolve(filepath), 'as:', sendname, 'with mimetype:', mimetype);117 118 setCookie(res, sendname.split(' ').shift());119 res.setHeader('Content-disposition', 'attachment; filename=' + sendname );120 res.setHeader('Content-type', mimetype);121 // can remove below since it's now handled in the noCache middleware in sails.config.http.middleware122 // prevent caching in the browser123 // res.setHeader('Expires', '-1');124 // res.setHeader('Pragma', 'no-cache');125 // res.setHeader('Cache-Control', 'Cache-Control', 'private, no-cache, no-store, must-revalidate');126 let filestream = fs.createReadStream( path.resolve(filepath) );127 try{128 filestream129 .on('end', function(){130 res.end && res.end()131 })132 .pipe(res, {end: false})133 }catch(err){134 console.error(err, 'with filestream')135 }136 return filestream137}138function setCookie(res, cookiename){139 let randomNumber=Math.random().toString();140 randomNumber=randomNumber.substring(2,randomNumber.length);141 res.cookie(cookiename,randomNumber, { maxAge: 900000, httpOnly: false });142}143function Queue(){144 events.EventEmitter.call(this);145} 146Queue.prototype = {147 __proto__ : events.EventEmitter.prototype148 , results : []149 , functions : []150 , pageorder: {}151};152module.exports.getMaps = function(req, res, opts){153 let query = req.query || {};154 let view = req.params ? req.params.view || query.view : 'www.google.com';155 let views = view ? [view] : undefined156 , viewname = views.join('---')157 , allparams = req.params && req.params.all() || req.query || {}158 , cacheID = Object.keys(allparams).map(key=>key!=='filetype' ? key + '=' + allparams[key] : '').join(' ')159 , filetype = allparams.filetype || '.jpg'160 ;161 opts = opts || {};162 let cached = cachedfiles[cacheID];163 if(cached){164 // console.log('sending cached item for view:', view, cachedfiles[view]);165 166 // console.log('cached is:');167 // console.dir(cached);168 169 let cachedItem = cached[filetype] || cached['.' + filetype];170 // console.log('cachedItem is')171 // console.dir(cachedItem);172 // if(/pdf/i.test(allparams.filetype)){173 // if(!cached.pdfpath)174 // return sendAsPDF.call({ view: cacheID }, cached, res, mapcomplete)175 // else176 // // return streamFile(cached.filepath, cached.filepath, res);177 // return streamFile(cached.pdfpath, cached.pdfname, res);178 // }179 180 if( cachedItem && fs.existsSync(cachedItem.filepath) ){181 console.log('sending cached item for view', 'for filetype:', filetype);182 return streamFile(cachedItem.filepath, cachedItem.name, res)183 }184 // return streamFile(cached.filepath, cached.name, res);185 }186 let queue = new Queue();187 queue.on('change', function(){188 let next = queue.functions.shift();189 next && next() || queue.removeAllListeners('change');190 })191 192 let concurrent = 0193 , done194 , accessnote = 'Accessed: ' + new Date().toLocaleDateString() + ' - ' + new Date().toLocaleTimeString()195 ;196 197 views.forEach(function(view, index){198 let func = function(){199 if(done || queue.results.length === views.length){200 done = true;201 queue.removeAllListeners('change');202 }203 let options = {204 accessnote : accessnote205 , pageNum : views.length > 1 ? index+1 : 1206 , numPages : views.length > 1 ? views.length +1 : 1 207 , height: req.param('height') || query.height208 , width: req.param('width') || query.width209 , format: req.param('format') || query.format210 , filetype: req.param('filetype') || query.filetype211 , orientation: req.param('orientation') || query.orientation212 , quality: req.param('quality') || query.quality213 , combined: req.param('combined')214 }215 // make a copy just in case it gets mutated216 let copy =JSON.parse(JSON.stringify(opts))217 for(let key in copy)218 options[key] = copy[key]219 concurrent +=1;220 console.log('concurrent renderers:', concurrent, 'now starting view: ', view)221 return makeMap(view, options, function(data){222 queue.results.push( data );223 queue.emit('change')224 queue.pageorder[options.pageNum] = data;225 concurrent -= 1;226 // console.log('concurrent renderers:', concurrent);227 if(queue.results.length === views.length || concurrent===0){228 done = true;229 let ordered = correctOrder( queue.pageorder )230 ordered.unshift( view )231 // ordered.unshift( views.length === 1 ? view : 'Responder Maps' )232 return makePDF.call({ view: cacheID, filetype: options.filetype, combine:options.combined },ordered, res, mapcomplete);233 }234 });235 }236 if(concurrent < 10 && !done){237 func();238 }else{239 queue.functions.push(func)240 }241 })242}243function correctOrder(ordermap){244 return Object.keys(ordermap).sort().map(function(index){245 return ordermap[index]246 })247}248function sendAsPDF(options, res, callback){249 let pdfpath = options.filepath.replace(/(\.png|\.jpg)$/i, '.pdf')250 , parts = pdfpath.split(/\/\\/g)251 , pdfname = parts.pop()252 , tempdir = parts.join('/')253 , deletes = []254 ;255 console.log(pdfpath, options.filepath, pdfname, tempdir)256 convertJPG(options.filepath, pdfname, done=>{257 // set name and path in the cache258 options.pdfpath = pdfpath;259 options.pdfname = options.name.replace(/(\.png|\.jpg)$/i, '.pdf');;260 deletes.push(()=>fs.unlink(options.filepath, cb=>console.log('deleted', options.filepath)) ) ;261 setTimeout(t=>{262 readyToSend.call(this, tempdir, pdfpath, options.pdfname, deletes, callback, res)263 }, 500)264 })265}266// GraphicsMagick page sizes from http://www.graphicsmagick.org/GraphicsMagick.html#details-page267// let pageSizes = {268// '11x17' : [792, 1224]269// , 'Ledger' : [1224, 792]270// , 'Legal' : [612, 1008]271// , 'Letter' : [612, 792]272// }273function makePDF(data, res, callback){274 // let jpgs = []275 // console.log('makePDF', 'this', this);276 let pdfName = data.shift() + '.pdf'277 , author = '{AUTHOR}'278 , date = new Date().toLocaleString()279 , pdf = new pdfDocument({280 layout : 'landscape'281 , Title: pdfName282 , title: pdfName283 , Author: author284 , author: author285 , CreationDate: date286 , creationDate: date287 })288 ;289 // console.log('data:', data);290 // if(!data instanceof Array){291 // // pdfName = data.sendname || ('Print Output' + new Date().toLocaleDateString())292 // data = [data]293 // }294 if(data.length > 1 && this.combine){295 tmp.dir({prefix: 'map-print' }, (err, tempdir)=>{296 if (err) throw err;297 298 // console.log("Dir: ", tempdir);299 let pdfname = (data[0].sendname || pdfName).replace(/(\.png|\.jpg)$/i, '.pdf') 300 , pdfpath = path.join(tempdir, pdfname)301 , promises = []302 , deletes = []303 , mergepdfs = []304 // , convertimages = []305 ;306 console.log('creating', pdfpath);307 let accessnote = 'Accessed :' + new Date().toLocaleDateString() + ' - ' 308 + new Date().toLocaleTimeString()309 ;310 // if(length > 1){311 data.forEach(el=>{312 if(el.filepath){313 let mimetype = mime.lookup(el.filepath);314 // pdfs315 if( ! ~ mimetype.indexOf('image') ){316 mergepdfs.push('"' + el.filepath + '"')317 }318 // else{319 // convertimages.push(el.filepath)320 // }321 }322 if(el.callback instanceof Function)323 deletes.push(el.callback)324 })325 // if(convertimages.length>0){326 // convertimages.forEach((image, index)=>{327 // let name = convertimages.length > 1 ? index+pdfname : pdfname328 // , filepath = path.join(tempdir, name)329 // ;330 // convertJPG(image, filepath, function(){331 // mergepdfs.push(filepath);332 // deletes.push(filepath);333 // // readyToSend.call(this, tempdir, pdfpath, pdfName, deletes, callback, res)334 // })335 // })336 // }337 if(mergepdfs.length>0){338 mergePDFs(mergepdfs, pdfpath, ()=>{339 readyToSend.call(this, tempdir, pdfpath, pdfname, deletes, callback, res)340 })341 }else{342 // setTimeout(t=>{343 readyToSend.call(this, tempdir, pdfpath, pdfname, deletes, callback, res)344 // }, 500)345 }346 })347 }348 else{349 // send the pdf and break out of the loop if this is the only pdf350 Q.fapply( ()=>{351 // console.log('callback is:')352 // console.dir(callback);353 callback.call(this, res, data[0])354 // mapcomplete.call(this, res, data[0])355 }).then(function(){356 deleteDir(tempdir)357 })358 return 359 }360}361function addPageFooter(pdf, pageNum, numPages, accessnote) {362 pdf.moveTo(500)363 pdf.text(accessnote, {align : 'left'} )364 pdf.moveTo(500, 200)365 // pdf.moveDown()366 pdf.text(pageNum + " / " + numPages, {align : 'right'} )367 // return "<img src='" + footerImageUrl + "' alt='Surly Labs' />" +368 // return "<table width='100%'>" + 369 // "<tr><td><small> " + accessnote + "</small></td>" +370 // "<td> </td> <td> </td>" +371 // "<td colspan='2' style='text-align: right;'><small>" + pageNum + " / " + numPages + "</small></td></tr>" +372 // "</table>";373 // "<tr><td colspan='1' style='text-align: center;'><small>ORGANIZATION | contact@organization.com</small></td></tr>" +374}375function readyToSend(tempdir, pdfpath, pdfName, deletes, callback, res){376 console.log('ready to send==========');377 let resdata = {378 filepath : pdfpath,379 sendname : pdfName,380 callback: function(){381 console.log('done!', 'now it`s time to delete', pdfpath);382 resdata.deletetimer = setTimeout(function(){383 deleteDir(tempdir);384 while(deletes.length>0){385 // console.log(deletes.length, 'still left to delete');386 let func = deletes.shift()387 if(func instanceof Function)388 func()389 }390 }, cachetimeout) // wait time specified in cachetimeout391 392 }393 }394 return callback.call(this, res, resdata);395}396function deleteDir(dir){397 rmdir.async(dir, function(err){398 if(err)399 return err// console.error('error', err, 'when trying to delete', dir)400 // return console.log(dir, 'deleted!')401 return402 })403}404function mergePDFs(pdfs, outfilepath, callback){405 // add other pdfs to the mapbook - eg. cover sheet, etc406 // pdfs.unshift('"/user/coversheet.pdf"');407 408 let cmd = 'gs -dNOPAUSE -sDEVICE=pdfwrite'409 + ' -sOUTPUTFILE="' + outfilepath + '"'410 + ' -dBATCH ' + pdfs.join(' ')411 ;412 console.log(cmd, pdfs.join(' '));413 let child = exec(cmd, function(error, stdout, stderr){414 console.log('stdout: ' + stdout);415 console.log('stderr: ' + stderr);416 if(error) {417 console.log('exec error: ' + error);418 return callback(error)419 }420 return callback()421 })422}423function convertJPG(image, outfilepath, callback){424 // add other pdfs to the mapbook - eg. cover sheet, etc425 // pdfs.unshift('"/user/coversheet.pdf"');426 427 let cmd = `img2pdf -s letter -o "${outfilepath}" "${image}"`428 429 console.log(cmd, image);430 let child = exec(cmd, function(error, stdout, stderr){431 console.log('stdout: ' + stdout);432 console.log('stderr: ' + stderr);433 if(error) {434 console.log('exec error: ' + error);435 return callback(error)436 }437 return callback()438 })...
WebSocket.gateway.ts
Source:WebSocket.gateway.ts
...53 attempts = [];54 };55 const openListener = () => {56 isConnected = true;57 cleanUpAttempts();58 pendingToSend.forEach(pending => socket?.send(pending));59 pendingToSend = [];60 };61 const closeListener = () => {62 isConnected = false;63 cleanUpAttempts();64 reconnect();65 };66 const messageListener = (event: MessageEvent) => {67 const data = JSON.parse(event.data);68 if (data.type === MESSAGE_RESPONSE_TYPE.UPDATE) {69 const eventListeners = listeners[data.resource] || [];70 eventListeners.forEach(listener => listener(data));71 }72 if (data.type === MESSAGE_RESPONSE_TYPE.SUCCESS && data.resource) {73 subscriptionIds[data.resource] = data?.message?.subscriptionId;74 }75 };76 const connect = () => {77 if (socket !== null) socket.close();...
Using AI Code Generation
1var tracetest = require('tracetest');2tracetest.cleanUpAttempts();3var tracetest = require('tracetest');4tracetest.cleanUpAttempts();5var tracetest = require('tracetest');6tracetest.cleanUpAttempts();7var tracetest = require('tracetest');8tracetest.cleanUpAttempts();9var tracetest = require('tracetest');10tracetest.cleanUpAttempts();11var tracetest = require('tracetest');12tracetest.cleanUpAttempts();13var tracetest = require('tracetest');14tracetest.cleanUpAttempts();15var tracetest = require('tracetest');16tracetest.cleanUpAttempts();17var tracetest = require('tracetest');18tracetest.cleanUpAttempts();19var tracetest = require('tracetest');20tracetest.cleanUpAttempts();21var tracetest = require('tracetest');22tracetest.cleanUpAttempts();23var tracetest = require('tracetest');24tracetest.cleanUpAttempts();25var tracetest = require('tracetest');26tracetest.cleanUpAttempts();27var tracetest = require('tracetest');28tracetest.cleanUpAttempts();29var tracetest = require('tracetest');30tracetest.cleanUpAttempts();
Using AI Code Generation
1var tracetest = require('./tracetest.js');2var trace = new tracetest.TraceTest();3trace.cleanUpAttempts();4function TraceTest() {5 this.cleanUpAttempts = function() {6 console.log('cleanUpAttempts');7 }8}9module.exports.TraceTest = TraceTest;10module.exports = {11 cleanUpAttempts: function() {12 console.log('cleanUpAttempts');13 }14}15module.exports.cleanUpAttempts = function() {16 console.log('cleanUpAttempts');17 }
Using AI Code Generation
1var tracetest = require('./tracetest.js');2tracetest.cleanUpAttempts();3var fs = require('fs');4var cleanUpAttempts = function() {5};6exports.cleanUpAttempts = cleanUpAttempts;
Using AI Code Generation
1var tracetest = require("./tracetest.js");2tracetest.cleanUpAttempts();3var fs = require("fs");4var path = require("path");5var testDir = path.join(__dirname, "test");6var attemptsFile = path.join(testDir, "attempts.json");7var cleanUpAttempts = function() {8 var attempts = {9 };10 fs.writeFileSync(attemptsFile, JSON.stringify(attempts));11};12module.exports = {13};14TypeError: Object function cleanUpAttempts() {15 var attempts = {16 };17 fs.writeFileSync(attemptsFile, JSON.stringify(attempts));18} has no method 'cleanUpAttempts'19TypeError: Object function cleanUpAttempts() {20 var attempts = {21 };22 fs.writeFileSync(attemptsFile, JSON.stringify(attempts));23} has no method 'cleanUpAttempts'24TypeError: Object function cleanUpAttempts() {25 var attempts = {26 };27 fs.writeFileSync(attemptsFile, JSON.stringify(attempts));28} has no method 'cleanUpAttempts'
Using AI Code Generation
1var trace = require('./tracetest.js');2trace.cleanUpAttempts();3var trace = require('./trace.js');4var fs = require('fs');5exports.cleanUpAttempts = function(){6 trace.cleanUpAttempts();7}8var fs = require('fs');9exports.cleanUpAttempts = function(){10 console.log("Cleaning up attempts");11}
Using AI Code Generation
1const trace = require('./tracetest');2const traceObj = new trace();3traceObj.cleanUpAttempts();4console.log(traceObj.attempts);5class trace {6 constructor() {7 this.attempts = 0;8 }9 cleanUpAttempts() {10 this.attempts = 0;11 }12}13module.exports = trace;14I have a file called test.js which is in a different folder than tracetest.js . I want to use the cleanUpAttempts() method from the tracetest.js file in the test.js file. I tried to do this by importing the tracetest.js file using require() but it doesn't work. I get the following error:15trace {16 cleanUpAttempts: [Function: cleanUpAttempts] }17class File {18 constructor(filename) {19 this.filename = filename;20 }21 read() {22 return fs.readFileSync(this.filename, "utf8").split("23");24 }25}26const file = new File("test.txt");27file.read();28class File {29 constructor(filename) {30 this.filename = filename;31 }32 read() {33 return fs.readFileSync(this.filename, "utf8").split("34");
Using AI Code Generation
1var trace = require('trace');2var trace1 = trace.startTrace('trace1');3trace1.addEvent('event1');4trace1.addEvent('event2');5trace1.addEvent('event3');6var trace2 = trace.startTrace('trace2');7trace2.addEvent('event1');8trace2.addEvent('event2');9trace2.addEvent('event3');10trace2.addEvent('event4');11trace3.addEvent('event1');12trace1.addEvent('event4');13trace1.endTrace();14trace2.endTrace();15trace3.endTrace();16trace1.endTrace();17trace2.endTrace();18trace3.endTrace();19trace1.addEvent('event4');20trace1.endTrace();21trace2.endTrace();22trace3.endTrace();23trace1.addEvent('event5');24trace1.endTrace();25trace2.endTrace();26trace3.endTrace();27trace1.addEvent('event6');28trace1.endTrace();29trace2.endTrace();30trace3.endTrace();31trace1.addEvent('event5');32trace1.addEvent('event6');33trace1.addEvent('event7');34trace1.addEvent('event8');35trace1.addEvent('event9');36trace1.addEvent('event10');37trace1.addEvent('event11');38trace1.addEvent('event12');39trace1.addEvent('event13');40trace1.addEvent('event14');41trace1.addEvent('event15');42trace1.addEvent('event16');43trace1.addEvent('event17');44trace1.addEvent('event18');45trace1.addEvent('event19');46trace1.addEvent('event20');47trace1.addEvent('event21');48trace1.addEvent('event22');49trace1.addEvent('event23');50trace1.addEvent('event24');51trace1.addEvent('event25');52trace1.addEvent('event26');53trace1.addEvent('event27');54trace1.addEvent('event28
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!