actionhero
Version:
The reusable, scalable, and quick node.js API server for stateless and stateful applications
331 lines (293 loc) • 11.7 kB
text/typescript
import { api, Process, specHelper, task } from "./../../src/index";
const actionhero = new Process();
describe("Core: specHelper", () => {
beforeAll(async () => {
await actionhero.start();
});
afterAll(async () => {
await actionhero.stop();
});
test("can make a request with just params", async () => {
const { randomNumber } = await specHelper.runAction("randomNumber");
expect(randomNumber).toBeGreaterThanOrEqual(0);
expect(randomNumber).toBeLessThan(1);
});
test("will stack up messages received", async () => {
const connection = await specHelper.buildConnection();
connection.params.thing = "stuff";
const { error } = await specHelper.runAction("x", connection);
expect(connection.messages).toHaveLength(2);
expect(connection.messages[0].welcome).toEqual(
"Hello! Welcome to the actionhero api"
);
expect(connection.messages[1].error).toEqual(
"Error: unknown action or invalid apiVersion"
);
expect(error).toEqual("Error: unknown action or invalid apiVersion");
});
describe("metadata, type-safety, and errors", () => {
beforeAll(() => {
api.actions.versions.stringResponseTestAction = [1];
api.actions.actions.stringResponseTestAction = {
//@ts-ignore
1: {
name: "stringResponseTestAction",
description: "stringResponseTestAction",
version: 1,
run: async (data) => {
//@ts-ignore
data.response = "something response";
},
},
};
api.actions.versions.stringErrorTestAction = [1];
api.actions.actions.stringErrorTestAction = {
//@ts-ignore
1: {
name: "stringErrorTestAction",
description: "stringErrorTestAction",
version: 1,
run: (data) => {
//@ts-ignore
data.response = "something response";
throw new Error("some error");
},
},
};
api.actions.versions.arrayResponseTestAction = [1];
api.actions.actions.arrayResponseTestAction = {
//@ts-ignore
1: {
name: "arrayResponseTestAction",
description: "arrayResponseTestAction",
version: 1,
run: async (data) => {
data.response = [1, 2, 3];
},
},
};
api.actions.versions.arrayErrorTestAction = [1];
api.actions.actions.arrayErrorTestAction = {
//@ts-ignore
1: {
name: "arrayErrorTestAction",
description: "arrayErrorTestAction",
version: 1,
run: (data) => {
data.response = [1, 2, 3];
throw new Error("some error");
},
},
};
});
afterAll(() => {
delete api.actions.actions.stringResponseTestAction;
delete api.actions.versions.stringResponseTestAction;
delete api.actions.actions.stringErrorTestAction;
delete api.actions.versions.stringErrorTestAction;
delete api.actions.actions.arrayResponseTestAction;
delete api.actions.versions.arrayResponseTestAction;
delete api.actions.actions.arrayErrorTestAction;
delete api.actions.versions.arrayErrorTestAction;
});
describe("happy-path", () => {
test("if the response payload is an object, it appends metadata", async () => {
const response = await specHelper.runAction("randomNumber");
expect(response.error).toBeUndefined();
expect(response.randomNumber).toBeTruthy();
expect(response.messageId).toBeTruthy();
expect(response.serverInformation.serverName).toEqual("actionhero");
expect(response.requesterInformation.remoteIP).toEqual("testServer");
});
test("if the response payload is a string, it maintains type", async () => {
const response = await specHelper.runAction("stringResponseTestAction");
expect(response).toEqual("something response");
expect(response.error).toBeUndefined();
expect(response.messageId).toBeUndefined();
expect(response.serverInformation).toBeUndefined();
expect(response.requesterInformation).toBeUndefined();
});
test("if the response payload is a array, it maintains type", async () => {
const response = await specHelper.runAction("arrayResponseTestAction");
expect(response).toEqual([1, 2, 3]);
expect(response.error).toBeUndefined();
expect(response.messageId).toBeUndefined();
expect(response.serverInformation).toBeUndefined();
expect(response.requesterInformation).toBeUndefined();
});
});
describe("disabling metadata", () => {
beforeAll(() => {
api.specHelper.returnMetadata = false;
});
afterAll(() => {
api.specHelper.returnMetadata = true;
});
test("if the response payload is an object, it should not append metadata", async () => {
const response = await specHelper.runAction("randomNumber");
expect(response.error).toBeUndefined();
expect(response.randomNumber).toBeTruthy();
expect(response.messageId).toBeUndefined();
expect(response.serverInformation).toBeUndefined();
expect(response.requesterInformation).toBeUndefined();
});
});
describe("errors", () => {
test("if the response payload is an object and there is an error, it appends metadata", async () => {
const response = await specHelper.runAction("x");
expect(response.error).toEqual(
"Error: unknown action or invalid apiVersion"
);
expect(response.messageId).toBeTruthy();
expect(response.serverInformation.serverName).toEqual("actionhero");
expect(response.requesterInformation.remoteIP).toEqual("testServer");
});
test("if the response payload is a string, just the error will be returned", async () => {
const response = await specHelper.runAction("stringErrorTestAction");
expect(response).toEqual("Error: some error");
expect(response.messageId).toBeUndefined();
expect(response.serverInformation).toBeUndefined();
expect(response.requesterInformation).toBeUndefined();
});
test("if the response payload is a array, just the error will be returned", async () => {
const response = await specHelper.runAction("arrayErrorTestAction");
expect(response).toEqual("Error: some error");
expect(response.messageId).toBeUndefined();
expect(response.serverInformation).toBeUndefined();
expect(response.requesterInformation).toBeUndefined();
});
});
});
describe("test responses", () => {
test("will not report a broken test as a broken action (sync)", async () => {
const response = await specHelper.runAction("randomNumber");
try {
response.not.a.real.thing();
throw new Error("should not get here");
} catch (e) {
expect(String(e)).toEqual(
"TypeError: Cannot read property 'a' of undefined"
);
}
});
test("will not report a broken test as a broken action (async)", async () => {
const response = await specHelper.runAction("sleepTest");
try {
response.not.a.real.thing();
throw new Error("should not get here");
} catch (e) {
expect(String(e)).toEqual(
"TypeError: Cannot read property 'a' of undefined"
);
}
});
test("messageId can be configurable", async () => {
const response = await specHelper.runAction("randomNumber", {
messageId: "aaa",
});
expect(response.messageId).toEqual("aaa");
});
});
describe("files", () => {
test("can request file data", async () => {
const data = await specHelper.getStaticFile("simple.html");
expect(data.error).toBeUndefined();
expect(data.content).toContain("<h1>Actionhero</h1>");
expect(data.mime).toEqual("text/html");
expect(data.length).toEqual(101);
});
test("missing files", async () => {
const data = await specHelper.getStaticFile("missing.html");
expect(data.error).toEqual("That file is not found");
expect(data.mime).toEqual("text/html");
expect(data.content).toBeNull();
});
});
describe("persistent test connections", () => {
let connection;
let connId;
const messageIds = [];
test("can make a request with a specified connection", async () => {
connection = await specHelper.buildConnection();
connection.params = {
key: "someKey",
value: "someValue",
};
connId = connection.id;
const response = await specHelper.runAction("cacheTest", connection);
messageIds.push(response.messageId);
expect(connection.messages).toHaveLength(2);
expect(connId).toEqual(connection.id);
expect(connection.fingerprint).toEqual(connId);
});
test("can make second request", async () => {
const response = await specHelper.runAction("randomNumber", connection);
messageIds.push(response.messageId);
expect(connection.messages).toHaveLength(3);
expect(connId).toEqual(connection.id);
expect(connection.fingerprint).toEqual(connId);
});
test("will generate new ids and fingerprints for a new connection", async () => {
const response = await specHelper.runAction("randomNumber");
messageIds.push(response.messageId);
expect(response.requesterInformation.id).not.toEqual(connId);
expect(response.requesterInformation.fingerprint).not.toEqual(connId);
});
test("message ids are unique", () => {
expect(messageIds).toHaveLength(3);
expect(messageIds[0]).not.toEqual(messageIds[1]);
expect(messageIds[1]).not.toEqual(messageIds[2]);
});
});
describe("tasks", () => {
let taskRan = false;
beforeAll(() => {
api.tasks.tasks.testTask = {
name: "testTask",
description: "task: test: " + Math.random(),
queue: "default",
frequency: 0,
plugins: [],
pluginOptions: {},
run: (api, params) => {
taskRan = true;
return "OK";
},
};
api.tasks.jobs.testTask = api.tasks.jobWrapper("testTask");
});
afterAll(() => {
delete api.testOutput;
delete api.tasks.tasks.testTask;
});
test("can run tasks", async () => {
const response = await specHelper.runTask("testTask", {});
expect(response).toEqual("OK");
expect(taskRan).toEqual(true);
});
describe("flushed redis", () => {
beforeEach(async () => {
await api.redis.clients.client.flushdb();
});
test("findEnqueuedTasks (normal queues)", async () => {
await task.enqueue("testTask", { a: 1 });
const foundTasks = await specHelper.findEnqueuedTasks("testTask");
expect(foundTasks.length).toBe(1);
expect(foundTasks[0].args[0]).toEqual({ a: 1 });
});
test("findEnqueuedTasks (delayed queues)", async () => {
await task.enqueueIn(1, "testTask", { a: 1 });
const foundTasks = await specHelper.findEnqueuedTasks("testTask");
expect(foundTasks.length).toBe(1);
expect(foundTasks[0].args[0]).toEqual({ a: 1 });
});
test("deleteEnqueuedTasks", async () => {
await task.enqueue("testTask", { a: 1 });
await task.enqueueAt(10, "testTask", { a: 1 });
await specHelper.deleteEnqueuedTasks("testTask", { a: 1 });
const foundTasks = await specHelper.findEnqueuedTasks("testTask");
expect(foundTasks.length).toBe(0);
});
});
});
});