node-resque
Version:
an opinionated implementation of resque in node
329 lines (280 loc) • 9.36 kB
text/typescript
import { Queue, Worker } from "../../src";
import specHelper from "../utils/specHelper";
const jobs = {
add: {
perform: (a, b) => {
return a + b;
},
},
badAdd: {
perform: () => {
throw new Error("Blue Smoke");
},
},
messWithData: {
perform: (a) => {
a.data = "new thing";
return a;
},
},
async: {
perform: async () => {
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
return "yay";
},
},
twoSeconds: {
perform: async () => {
await new Promise((resolve) => {
setTimeout(resolve, 1000 * 2);
});
return "slow";
},
},
quickDefine: async () => {
return "ok";
},
};
let worker: Worker;
let queue: Queue;
describe("worker", () => {
afterAll(async () => {
await specHelper.disconnect();
});
test("can connect", async () => {
const worker = new Worker(
{ connection: specHelper.connectionDetails, queues: [specHelper.queue] },
{}
);
await worker.connect();
await worker.end();
});
test(
"can provide an error if connection failed",
async (done) => {
const connectionDetails = {
pkg: specHelper.connectionDetails.pkg,
host: "wronghostname",
password: specHelper.connectionDetails.password,
port: specHelper.connectionDetails.port,
database: specHelper.connectionDetails.database,
namespace: specHelper.connectionDetails.namespace,
};
const worker = new Worker(
{
connection: connectionDetails,
timeout: specHelper.timeout,
queues: [specHelper.queue],
},
{}
);
worker.on("error", async (error) => {
expect(error.message).toMatch(/ENOTFOUND|ETIMEDOUT|ECONNREFUSED/);
await worker.end();
done();
});
worker.connect();
},
30 * 1000
);
describe("performInline", () => {
beforeAll(() => {
worker = new Worker(
{
connection: specHelper.connectionDetails,
timeout: specHelper.timeout,
queues: [specHelper.queue],
},
jobs
);
});
test("can run a successful job", async () => {
const result = await worker.performInline("add", [1, 2]);
expect(result).toBe(3);
});
test("can run a successful async job", async () => {
const result = await worker.performInline("async");
expect(result).toBe("yay");
});
test("can run a failing job", async () => {
try {
await worker.performInline("badAdd", [1, 2]);
throw new Error("should not get here");
} catch (error) {
expect(String(error)).toBe("Error: Blue Smoke");
}
});
});
describe("[with connection]", () => {
beforeAll(async () => {
await specHelper.connect();
queue = new Queue({ connection: specHelper.connectionDetails }, {});
await queue.connect();
});
afterAll(async () => {
await specHelper.cleanup();
});
test("can boot and stop", async () => {
worker = new Worker(
{
connection: specHelper.connectionDetails,
timeout: specHelper.timeout,
queues: [specHelper.queue],
},
jobs
);
await worker.connect();
await worker.start();
await worker.end();
});
test("will determine the proper queue names", async () => {
const worker = new Worker(
{
connection: specHelper.connectionDetails,
timeout: specHelper.timeout,
},
jobs
);
await worker.connect();
expect(worker.queues).toEqual([]);
await queue.enqueue(specHelper.queue, "badAdd", [1, 2]);
await worker.checkQueues();
expect(worker.queues).toEqual([specHelper.queue]);
//@ts-ignore
await queue.del(specHelper.queue);
await worker.end();
});
describe("integration", () => {
test("will notice new job queues when started with queues=*", async (done) => {
const wildcardWorker = new Worker(
{
connection: specHelper.connectionDetails,
timeout: specHelper.timeout,
queues: ["*"],
},
jobs
);
await wildcardWorker.connect();
await wildcardWorker.start();
setTimeout(async () => {
await queue.enqueue("__newQueue", "add", [1, 2]);
}, 501);
wildcardWorker.on("success", async (q, job, result, duration) => {
expect(q).toBe("__newQueue");
expect(job.class).toBe("add");
expect(result).toBe(3);
expect(wildcardWorker.result).toBe(result);
expect(duration).toBeGreaterThanOrEqual(0);
wildcardWorker.removeAllListeners("success");
await wildcardWorker.end();
done();
});
});
describe("with worker", () => {
beforeEach(async () => {
worker = new Worker(
{
connection: specHelper.connectionDetails,
timeout: specHelper.timeout,
queues: [specHelper.queue],
},
jobs
);
await worker.connect();
});
afterEach(async () => {
await worker.end();
});
test("will mark a job as failed", async (done) => {
await queue.enqueue(specHelper.queue, "badAdd", [1, 2]);
await worker.start();
worker.on("failure", (q, job, failire) => {
expect(q).toBe(specHelper.queue);
expect(job.class).toBe("badAdd");
expect(failire.message).toBe("Blue Smoke");
worker.removeAllListeners("failure");
done();
});
});
test("can work a job and return successful things", async (done) => {
await queue.enqueue(specHelper.queue, "add", [1, 2]);
worker.start();
worker.on("success", (q, job, result, duration) => {
expect(q).toBe(specHelper.queue);
expect(job.class).toBe("add");
expect(result).toBe(3);
expect(worker.result).toBe(result);
expect(duration).toBeGreaterThanOrEqual(0);
worker.removeAllListeners("success");
done();
});
});
// TODO: Typescript seems to have trouble with frozen objects
// test('job arguments are immutable', async (done) => {
// await queue.enqueue(specHelper.queue, 'messWithData', { a: 'starting value' })
// worker.start()
// worker.on('success', (q, job, result) => {
// expect(result.a).toBe('starting value')
// expect(worker.result).toBe(result)
// worker.removeAllListeners('success')
// done()
// })
// })
test("can accept jobs that are simple functions", async (done) => {
await queue.enqueue(specHelper.queue, "quickDefine");
worker.start();
worker.on("success", (q, job, result, duration) => {
expect(result).toBe("ok");
expect(duration).toBeGreaterThanOrEqual(0);
worker.removeAllListeners("success");
done();
});
});
test("will not work jobs that are not defined", async (done) => {
await queue.enqueue(specHelper.queue, "somethingFake");
worker.start();
worker.on("failure", (q, job, failure, duration) => {
expect(q).toBe(specHelper.queue);
expect(String(failure)).toBe(
'Error: No job defined for class "somethingFake"'
);
expect(duration).toBeGreaterThanOrEqual(0);
worker.removeAllListeners("failure");
done();
});
});
test("will place failed jobs in the failed queue", async () => {
let data = await specHelper.redis.rpop(
specHelper.namespace + ":" + "failed"
);
data = JSON.parse(data);
expect(data.queue).toBe(specHelper.queue);
expect(data.exception).toBe("Error");
expect(data.error).toBe('No job defined for class "somethingFake"');
});
test("will ping with status even when working a slow job", async (done) => {
const nowInSeconds = Math.round(new Date().getTime() / 1000);
await worker.start();
await new Promise((resolve) =>
setTimeout(resolve, worker.options.timeout * 2)
);
const pingKey = worker.connection.key("worker", "ping", worker.name);
const firstPayload = JSON.parse(await specHelper.redis.get(pingKey));
expect(firstPayload.name).toEqual(worker.name);
expect(firstPayload.time).toBeGreaterThanOrEqual(nowInSeconds);
await queue.enqueue(specHelper.queue, "twoSeconds");
worker.on("success", (q, job, result) => {
expect(result).toBe("slow");
worker.removeAllListeners("success");
done();
});
const secondPayload = JSON.parse(await specHelper.redis.get(pingKey));
expect(secondPayload.name).toEqual(worker.name);
expect(secondPayload.time).toBeGreaterThanOrEqual(firstPayload.time);
});
});
});
});
});