UNPKG

node-resque

Version:

an opinionated implementation of resque in node

373 lines (322 loc) 10.2 kB
import specHelper from "../utils/specHelper"; import { Scheduler, Plugins, Queue, Worker, Job, ParsedFailedJobPayload, } from "../../src"; let queue: Queue; let scheduler: Scheduler; const jobs: { [key: string]: Job<any> } = { brokenJob: { plugins: [Plugins.Retry], pluginOptions: { Retry: { retryLimit: 3, retryDelay: 100, }, }, perform: () => { throw new Error("BUSTED"); }, }, happyJob: { plugins: [Plugins.Retry], pluginOptions: { Retry: { retryLimit: 3, retryDelay: 100, }, }, //@ts-ignore perform: () => { // no return }, }, }; describe("plugins", () => { describe("retry", () => { beforeAll(async () => { await specHelper.connect(); await specHelper.cleanup(); queue = new Queue( { connection: specHelper.cleanConnectionDetails(), queue: [specHelper.queue], }, jobs, ); scheduler = new Scheduler({ connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, }); await scheduler.connect(); scheduler.start(); await queue.connect(); }); afterAll(async () => { await scheduler.end(); await queue.end(); await specHelper.disconnect(); }); afterEach(async () => { await specHelper.cleanup(); }); test("will work fine with non-crashing jobs", async () => { await new Promise(async (resolve) => { await queue.enqueue(specHelper.queue, "happyJob", [1, 2]); const length = await queue.length(specHelper.queue); expect(length).toBe(1); const worker = new Worker( { connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, queues: [specHelper.queue], }, jobs, ); worker.on("failure", () => { throw new Error("should not get here"); }); await worker.connect(); worker.on("success", async () => { const length = await specHelper.redis.llen( `${specHelper.namespace}:failed`, ); expect(length).toBe(0); await worker.end(); resolve(null); }); worker.start(); }); }); test("will retry the job n times before finally failing", async () => { await new Promise(async (resolve) => { await queue.enqueue(specHelper.queue, "brokenJob"); const length = await queue.length(specHelper.queue); expect(length).toBe(1); let failButRetryCount = 0; let failureCount = 0; const worker = new Worker( { connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, queues: [specHelper.queue], }, jobs, ); worker.on("success", () => { failButRetryCount++; }); await worker.connect(); worker.on("failure", async () => { failureCount++; expect(failButRetryCount).toBe(2); expect(failureCount).toBe(1); expect(failButRetryCount + failureCount).toBe(3); const length = await specHelper.redis.llen( `${specHelper.namespace}:failed`, ); expect(length).toBe(1); await worker.end(); resolve(null); }); worker.start(); }); }); test("can have a retry count set", async () => { await new Promise(async (resolve) => { const customJobs = { jobWithRetryCount: { plugins: [Plugins.Retry], pluginOptions: { Retry: { retryLimit: 5, retryDelay: 100, }, }, perform: () => { throw new Error("BUSTED"); }, }, }; await queue.enqueue(specHelper.queue, "jobWithRetryCount", [1, 2]); const length = await queue.length(specHelper.queue); expect(length).toBe(1); let failButRetryCount = 0; let failureCount = 0; const worker = new Worker( { connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, queues: [specHelper.queue], }, customJobs, ); worker.on("success", () => { failButRetryCount++; }); await worker.connect(); worker.on("failure", async () => { failureCount++; expect(failButRetryCount).toBe(4); expect(failureCount).toBe(1); expect(failButRetryCount + failureCount).toBe(5); const length = await specHelper.redis.llen( `${specHelper.namespace}:failed`, ); expect(length).toBe(1); await worker.end(); resolve(null); }); worker.start(); }); }); test("can have custom retry times set", async () => { await new Promise(async (resolve) => { const customJobs = { //@ts-ignore jobWithBackoffStrategy: { plugins: [Plugins.Retry], pluginOptions: { Retry: { retryLimit: 5, backoffStrategy: [1, 2, 3, 4, 5], }, }, perform: function (a, b, callback) { callback(new Error("BUSTED"), null); }, } as Job<any>, }; await queue.enqueue(specHelper.queue, "jobWithBackoffStrategy", [1, 2]); const length = await queue.length(specHelper.queue); expect(length).toBe(1); let failButRetryCount = 0; let failureCount = 0; const worker = new Worker( { connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, queues: [specHelper.queue], }, customJobs, ); worker.on("success", () => { failButRetryCount++; }); await worker.connect(); worker.on("failure", async () => { failureCount++; expect(failButRetryCount).toBe(4); expect(failureCount).toBe(1); expect(failButRetryCount + failureCount).toBe(5); const length = await specHelper.redis.llen( `${specHelper.namespace}:failed`, ); expect(length).toBe(1); await worker.end(); resolve(null); }); worker.start(); }); }); test("when a job fails it should be re-enqueued (and not go to the failure queue)", async () => { await new Promise(async (resolve) => { await queue.enqueue(specHelper.queue, "brokenJob", [1, 2]); const worker = new Worker( { connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, queues: [specHelper.queue], }, jobs, ); await worker.connect(); worker.on("success", async () => { const timestamps = await queue.scheduledAt( specHelper.queue, "brokenJob", [1, 2], ); expect(timestamps.length).toBe(1); const length = await specHelper.redis.llen( `${specHelper.namespace}:failed`, ); expect(length).toBe(0); await worker.end(); resolve(null); }); worker.start(); }); }); test("will handle the stats properly for failing jobs", async () => { await new Promise(async (resolve) => { await queue.enqueue(specHelper.queue, "brokenJob", [1, 2]); const worker = new Worker( { connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, queues: [specHelper.queue], }, jobs, ); await worker.connect(); worker.on("success", async () => { const globalProcessed = await specHelper.redis.get( `${specHelper.namespace}:stat:processed`, ); const globalFailed = await specHelper.redis.get( `${specHelper.namespace}:stat:failed`, ); const workerProcessed = await specHelper.redis.get( `${specHelper.namespace}:stat:processed:${worker.name}`, ); const workerFailed = await specHelper.redis.get( `${specHelper.namespace}:stat:failed:${worker.name}`, ); expect(String(globalProcessed)).toBe("0"); expect(String(globalFailed)).toBe("1"); expect(String(workerProcessed)).toBe("0"); expect(String(workerFailed)).toBe("1"); await worker.end(); resolve(null); }); worker.start(); }); }); test("will set the retry counter & retry data", async () => { await new Promise(async (resolve) => { await queue.enqueue(specHelper.queue, "brokenJob", [1, 2]); const worker = new Worker( { connection: specHelper.cleanConnectionDetails(), timeout: specHelper.timeout, queues: [specHelper.queue], }, jobs, ); await worker.connect(); worker.on("success", async () => { const retryAttempts = await specHelper.redis.get( `${specHelper.namespace}:resque-retry:brokenJob:1-2`, ); let failureData = await specHelper.redis.get( `${specHelper.namespace}:failure-resque-retry:brokenJob:1-2`, ); expect(String(retryAttempts)).toBe("0"); const failure = JSON.parse(failureData) as ParsedFailedJobPayload; expect(failure.payload).toEqual([1, 2]); expect(failure.exception).toBe("Error: BUSTED"); expect(failure.worker).toBe("brokenJob"); expect(failure.queue).toBe("test_queue"); await worker.end(); resolve(null); }); worker.start(); }); }); }); });