0z2i6v3u5t 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/devcontainer.json +4 -0
- package/.devcontainer/setup.sh +11 -0
- package/.dockerignore +2 -0
- package/.github/CONTRIBUTING.md +52 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +43 -0
- package/.github/dependabot.yml +17 -0
- package/.github/workflows/codeql.yml +76 -0
- package/.github/workflows/publish_docs.yml +25 -0
- package/.github/workflows/test.yml +78 -0
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/.prettierrc +1 -0
- package/.vscode/launch.json +42 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/Dockerfile +17 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/SECURITY.md +5 -0
- package/__tests__/actions/cacheTest.ts +58 -0
- package/__tests__/actions/randomNumber.ts +26 -0
- package/__tests__/actions/recursiveAction.ts +16 -0
- package/__tests__/actions/sleepTest.ts +24 -0
- package/__tests__/actions/status.ts +17 -0
- package/__tests__/actions/swagger.ts +76 -0
- package/__tests__/actions/validationTest.ts +63 -0
- package/__tests__/cli/cli.ts +126 -0
- package/__tests__/core/api.ts +632 -0
- package/__tests__/core/cache.ts +400 -0
- package/__tests__/core/chatRoom.ts +589 -0
- package/__tests__/core/cli.ts +349 -0
- package/__tests__/core/cluster.ts +132 -0
- package/__tests__/core/config.ts +78 -0
- package/__tests__/core/errors.ts +112 -0
- package/__tests__/core/log.ts +23 -0
- package/__tests__/core/middleware.ts +427 -0
- package/__tests__/core/plugins/partialPlugin.ts +94 -0
- package/__tests__/core/plugins/withPlugin.ts +88 -0
- package/__tests__/core/plugins/withoutPlugin.ts +81 -0
- package/__tests__/core/process.ts +42 -0
- package/__tests__/core/specHelper.ts +330 -0
- package/__tests__/core/staticFile/compression.ts +99 -0
- package/__tests__/core/staticFile/staticFile.ts +180 -0
- package/__tests__/core/tasks/customQueueFunction.ts +67 -0
- package/__tests__/core/tasks/fullWorkerFlow.ts +199 -0
- package/__tests__/core/tasks/tasks.ts +605 -0
- package/__tests__/integration/browser.ts +133 -0
- package/__tests__/integration/ioredis-mock.ts +194 -0
- package/__tests__/integration/sendBuffer.ts +97 -0
- package/__tests__/integration/sendFile.ts +24 -0
- package/__tests__/integration/sharedFingerprint.ts +82 -0
- package/__tests__/integration/taskFlow.ts +110 -0
- package/__tests__/jest.ts +5 -0
- package/__tests__/modules/action.ts +103 -0
- package/__tests__/modules/config.ts +19 -0
- package/__tests__/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +24 -0
- package/__tests__/servers/web/allowedRequestHosts.ts +88 -0
- package/__tests__/servers/web/enableMultiples.ts +83 -0
- package/__tests__/servers/web/fileUpload.ts +79 -0
- package/__tests__/servers/web/jsonp.ts +57 -0
- package/__tests__/servers/web/nonMultiples.ts +83 -0
- package/__tests__/servers/web/rawBody.ts +208 -0
- package/__tests__/servers/web/returnErrorCodes.ts +55 -0
- package/__tests__/servers/web/routes/deepRoutes.ts +96 -0
- package/__tests__/servers/web/routes/routes.ts +579 -0
- package/__tests__/servers/web/routes/veryDeepRoutes.ts +92 -0
- package/__tests__/servers/web/web.ts +1031 -0
- package/__tests__/servers/websocket.ts +795 -0
- package/__tests__/tasks/runAction.ts +37 -0
- package/__tests__/template.ts.example +20 -0
- package/__tests__/testCliCommands/hello.ts +44 -0
- package/__tests__/testPlugin/public/plugin.html +1 -0
- package/__tests__/testPlugin/src/actions/pluginAction.ts +14 -0
- package/__tests__/testPlugin/src/bin/hello.ts +22 -0
- package/__tests__/testPlugin/src/initializers/pluginInitializer.ts +17 -0
- package/__tests__/testPlugin/src/tasks/pluginTask.ts +15 -0
- package/__tests__/testPlugin/tsconfig.json +10 -0
- package/__tests__/utils/utils.ts +492 -0
- package/app.json +23 -0
- package/bin/deploy-docs +39 -0
- package/client/ActionheroWebsocketClient.js +277 -0
- package/docker-compose.yml +73 -0
- package/package.json +24 -0
- package/public/chat.html +194 -0
- package/public/css/cosmo.css +12 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +115 -0
- package/public/javascript/.gitkeep +0 -0
- package/public/linkedSession.html +80 -0
- package/public/logo/actionhero-small.png +0 -0
- package/public/logo/actionhero.png +0 -0
- package/public/pixel.gif +0 -0
- package/public/simple.html +2 -0
- package/public/swagger.html +32 -0
- package/public/websocketLoadTest.html +322 -0
- package/src/actions/cacheTest.ts +58 -0
- package/src/actions/createChatRoom.ts +20 -0
- package/src/actions/randomNumber.ts +17 -0
- package/src/actions/recursiveAction.ts +13 -0
- package/src/actions/sendFile.ts +12 -0
- package/src/actions/sleepTest.ts +40 -0
- package/src/actions/status.ts +73 -0
- package/src/actions/swagger.ts +155 -0
- package/src/actions/validationTest.ts +36 -0
- package/src/bin/actionhero.ts +225 -0
- package/src/bin/methods/actions/list.ts +30 -0
- package/src/bin/methods/console.ts +26 -0
- package/src/bin/methods/generate/action.ts +58 -0
- package/src/bin/methods/generate/cli.ts +51 -0
- package/src/bin/methods/generate/initializer.ts +54 -0
- package/src/bin/methods/generate/plugin.ts +57 -0
- package/src/bin/methods/generate/server.ts +38 -0
- package/src/bin/methods/generate/task.ts +68 -0
- package/src/bin/methods/generate.ts +176 -0
- package/src/bin/methods/task/enqueue.ts +35 -0
- package/src/classes/action.ts +98 -0
- package/src/classes/actionProcessor.ts +463 -0
- package/src/classes/api.ts +51 -0
- package/src/classes/cli.ts +67 -0
- package/src/classes/config.ts +15 -0
- package/src/classes/connection.ts +321 -0
- package/src/classes/exceptionReporter.ts +9 -0
- package/src/classes/initializer.ts +59 -0
- package/src/classes/initializers.ts +5 -0
- package/src/classes/input.ts +9 -0
- package/src/classes/inputs.ts +34 -0
- package/src/classes/process/actionheroVersion.ts +15 -0
- package/src/classes/process/env.ts +16 -0
- package/src/classes/process/id.ts +34 -0
- package/src/classes/process/pid.ts +32 -0
- package/src/classes/process/projectRoot.ts +16 -0
- package/src/classes/process/typescript.ts +47 -0
- package/src/classes/process.ts +479 -0
- package/src/classes/server.ts +251 -0
- package/src/classes/task.ts +87 -0
- package/src/config/api.ts +107 -0
- package/src/config/errors.ts +162 -0
- package/src/config/logger.ts +113 -0
- package/src/config/plugins.ts +37 -0
- package/src/config/redis.ts +78 -0
- package/src/config/routes.ts +44 -0
- package/src/config/tasks.ts +84 -0
- package/src/config/web.ts +136 -0
- package/src/config/websocket.ts +62 -0
- package/src/index.ts +46 -0
- package/src/initializers/actions.ts +125 -0
- package/src/initializers/chatRoom.ts +214 -0
- package/src/initializers/connections.ts +124 -0
- package/src/initializers/exceptions.ts +155 -0
- package/src/initializers/params.ts +52 -0
- package/src/initializers/redis.ts +191 -0
- package/src/initializers/resque.ts +248 -0
- package/src/initializers/routes.ts +229 -0
- package/src/initializers/servers.ts +134 -0
- package/src/initializers/specHelper.ts +195 -0
- package/src/initializers/staticFile.ts +253 -0
- package/src/initializers/tasks.ts +188 -0
- package/src/modules/action.ts +89 -0
- package/src/modules/cache.ts +326 -0
- package/src/modules/chatRoom.ts +321 -0
- package/src/modules/config.ts +246 -0
- package/src/modules/log.ts +62 -0
- package/src/modules/redis.ts +93 -0
- package/src/modules/route.ts +59 -0
- package/src/modules/specHelper.ts +182 -0
- package/src/modules/task.ts +527 -0
- package/src/modules/utils/argv.ts +3 -0
- package/src/modules/utils/arrayStartingMatch.ts +21 -0
- package/src/modules/utils/arrayUnique.ts +15 -0
- package/src/modules/utils/collapseObjectToArray.ts +33 -0
- package/src/modules/utils/deepCopy.ts +3 -0
- package/src/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +19 -0
- package/src/modules/utils/eventLoopDelay.ts +34 -0
- package/src/modules/utils/fileUtils.ts +119 -0
- package/src/modules/utils/filterObjectForLogging.ts +51 -0
- package/src/modules/utils/filterResponseForLogging.ts +53 -0
- package/src/modules/utils/getExternalIPAddress.ts +17 -0
- package/src/modules/utils/hashMerge.ts +63 -0
- package/src/modules/utils/isPlainObject.ts +45 -0
- package/src/modules/utils/isRunning.ts +7 -0
- package/src/modules/utils/parseCookies.ts +20 -0
- package/src/modules/utils/parseHeadersForClientAddress.ts +53 -0
- package/src/modules/utils/parseIPv6URI.ts +24 -0
- package/src/modules/utils/replaceDistWithSrc.ts +9 -0
- package/src/modules/utils/safeGlob.ts +6 -0
- package/src/modules/utils/sleep.ts +8 -0
- package/src/modules/utils/sortGlobalMiddleware.ts +17 -0
- package/src/modules/utils/sourceRelativeLinkPath.ts +29 -0
- package/src/modules/utils.ts +66 -0
- package/src/server.ts +20 -0
- package/src/servers/web.ts +894 -0
- package/src/servers/websocket.ts +304 -0
- package/src/tasks/runAction.ts +29 -0
- package/tea.yaml +9 -0
- package/templates/README.md.template +17 -0
- package/templates/action.ts.template +15 -0
- package/templates/boot.js.template +9 -0
- package/templates/cli.ts.template +15 -0
- package/templates/gitignore.template +23 -0
- package/templates/initializer.ts.template +17 -0
- package/templates/package-plugin.json.template +12 -0
- package/templates/package.json.template +45 -0
- package/templates/projectMap.txt +39 -0
- package/templates/projectServer.ts.template +20 -0
- package/templates/server.ts.template +37 -0
- package/templates/task.ts.template +16 -0
- package/templates/test/action.ts.template +13 -0
- package/templates/test/task.ts.template +20 -0
- package/tsconfig.json +11 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
import * as path from "path";
|
2
|
+
import * as fs from "fs";
|
3
|
+
import * as Puppeteer from "puppeteer";
|
4
|
+
import { api, Process, config, utils } from "../../src/index";
|
5
|
+
import { PackageJson } from "type-fest";
|
6
|
+
|
7
|
+
const packageJSON: PackageJson = JSON.parse(
|
8
|
+
fs.readFileSync(path.join(__dirname, "..", "..", "package.json")).toString(),
|
9
|
+
);
|
10
|
+
|
11
|
+
let browser: Puppeteer.Browser;
|
12
|
+
let page: Puppeteer.Page;
|
13
|
+
let url: string;
|
14
|
+
|
15
|
+
describe("browser integration tests", () => {
|
16
|
+
let actionhero: Process;
|
17
|
+
|
18
|
+
beforeAll(async () => {
|
19
|
+
actionhero = new Process();
|
20
|
+
await actionhero.start();
|
21
|
+
await api.redis.clients.client.flushdb();
|
22
|
+
browser = await Puppeteer.launch({
|
23
|
+
headless: "new",
|
24
|
+
args: ["--no-sandbox"],
|
25
|
+
});
|
26
|
+
page = await browser.newPage();
|
27
|
+
});
|
28
|
+
|
29
|
+
afterAll(async () => {
|
30
|
+
await browser.close();
|
31
|
+
await actionhero.stop();
|
32
|
+
});
|
33
|
+
|
34
|
+
describe("default index page", () => {
|
35
|
+
beforeAll(() => {
|
36
|
+
url = `http://localhost:${config.web!.port}`;
|
37
|
+
});
|
38
|
+
|
39
|
+
test("loads the page", async () => {
|
40
|
+
await page.goto(url);
|
41
|
+
await utils.sleep(1000);
|
42
|
+
const title = await page.$eval("h1", (e) => e.textContent);
|
43
|
+
expect(title).toEqual("Your Actionhero Server is working.");
|
44
|
+
});
|
45
|
+
|
46
|
+
test("server status is loaded", async () => {
|
47
|
+
await page.goto(url);
|
48
|
+
await page.waitForSelector("#serverName");
|
49
|
+
const serverName = await page.$eval("#serverName", (e) => e.textContent);
|
50
|
+
expect(serverName).toEqual("actionhero");
|
51
|
+
|
52
|
+
const actionheroVersion = await page.$eval(
|
53
|
+
"#actionheroVersion",
|
54
|
+
(e) => e.textContent,
|
55
|
+
);
|
56
|
+
expect(actionheroVersion).toEqual(packageJSON.version);
|
57
|
+
});
|
58
|
+
});
|
59
|
+
|
60
|
+
describe("swagger page", () => {
|
61
|
+
beforeAll(() => {
|
62
|
+
url = `http://localhost:${config.web!.port}/swagger.html`;
|
63
|
+
});
|
64
|
+
|
65
|
+
test("loads the page", async () => {
|
66
|
+
await page.goto(url);
|
67
|
+
await page.waitForSelector("h2");
|
68
|
+
const title = await page.$eval("h2", (e) => e.textContent);
|
69
|
+
expect(title).toMatch(/^actionhero/);
|
70
|
+
});
|
71
|
+
|
72
|
+
test("documentation is loaded", async () => {
|
73
|
+
await page.goto(url);
|
74
|
+
await page.waitForSelector("h3");
|
75
|
+
const actionNames = await page.$$eval("h3", (elements) =>
|
76
|
+
elements.map((e) => e.textContent),
|
77
|
+
);
|
78
|
+
expect(actionNames.sort()).toEqual([
|
79
|
+
"createChatRoom",
|
80
|
+
"status",
|
81
|
+
"swagger",
|
82
|
+
]);
|
83
|
+
});
|
84
|
+
});
|
85
|
+
|
86
|
+
describe("chat test page", () => {
|
87
|
+
let sessionIDCookie: Puppeteer.Protocol.Network.Cookie;
|
88
|
+
|
89
|
+
test("I can be assigned a session on another page", async () => {
|
90
|
+
sessionIDCookie = (await page.cookies()).filter(
|
91
|
+
(c) => c.name === "sessionID",
|
92
|
+
)[0];
|
93
|
+
expect(sessionIDCookie.value).toBeTruthy();
|
94
|
+
});
|
95
|
+
|
96
|
+
describe("on the chat page", () => {
|
97
|
+
beforeAll(() => {
|
98
|
+
url = `http://localhost:${config.web!.port}/chat.html`;
|
99
|
+
});
|
100
|
+
|
101
|
+
test("can connect", async () => {
|
102
|
+
await page.goto(url);
|
103
|
+
await utils.sleep(2000);
|
104
|
+
const chat = await page.$eval("#chatBox", (e) => e.textContent);
|
105
|
+
expect(chat).toContain("Welcome to the actionhero api");
|
106
|
+
});
|
107
|
+
|
108
|
+
test("can chat", async () => {
|
109
|
+
const chatForm = await page.$("#message");
|
110
|
+
await chatForm!.type("hello world");
|
111
|
+
const chatSubmit = await page.$("#submitButton");
|
112
|
+
await chatSubmit!.click();
|
113
|
+
|
114
|
+
await utils.sleep(1000);
|
115
|
+
|
116
|
+
const chat = await page.$eval("#chatBox", (e) => e.textContent);
|
117
|
+
expect(chat).toContain("hello world");
|
118
|
+
});
|
119
|
+
|
120
|
+
test("has the same fingerprint", async () => {
|
121
|
+
const thisSessionID = (await page.cookies()).filter(
|
122
|
+
(c) => c.name === "sessionID",
|
123
|
+
)[0];
|
124
|
+
expect(thisSessionID.value).toEqual(sessionIDCookie.value);
|
125
|
+
const fingerprintFromWebSocket = await page.$eval(
|
126
|
+
"#fingerprint",
|
127
|
+
(e) => e.textContent,
|
128
|
+
);
|
129
|
+
expect(fingerprintFromWebSocket).toEqual(sessionIDCookie.value);
|
130
|
+
});
|
131
|
+
});
|
132
|
+
});
|
133
|
+
});
|
@@ -0,0 +1,194 @@
|
|
1
|
+
const MockIORedis = require("ioredis-mock");
|
2
|
+
|
3
|
+
import {
|
4
|
+
api,
|
5
|
+
cache,
|
6
|
+
chatRoom,
|
7
|
+
Process,
|
8
|
+
specHelper,
|
9
|
+
utils,
|
10
|
+
Task,
|
11
|
+
task,
|
12
|
+
} from "./../../src/index";
|
13
|
+
|
14
|
+
jest.mock("./../../src/config/redis.ts", () => ({
|
15
|
+
__esModule: true,
|
16
|
+
test: {
|
17
|
+
redis: () => {
|
18
|
+
const konstructor = MockIORedis;
|
19
|
+
const args = [{ port: 123, database: 1 }];
|
20
|
+
return {
|
21
|
+
_toExpand: false,
|
22
|
+
scanCount: 1000,
|
23
|
+
client: {
|
24
|
+
konstructor,
|
25
|
+
buildNew: true,
|
26
|
+
args,
|
27
|
+
},
|
28
|
+
subscriber: {
|
29
|
+
konstructor,
|
30
|
+
buildNew: true,
|
31
|
+
args,
|
32
|
+
},
|
33
|
+
tasks: {
|
34
|
+
konstructor,
|
35
|
+
buildNew: true,
|
36
|
+
args,
|
37
|
+
},
|
38
|
+
};
|
39
|
+
},
|
40
|
+
},
|
41
|
+
}));
|
42
|
+
|
43
|
+
jest.mock("./../../src/config/tasks.ts", () => ({
|
44
|
+
__esModule: true,
|
45
|
+
test: {
|
46
|
+
tasks: () => {
|
47
|
+
return {
|
48
|
+
scheduler: true,
|
49
|
+
queues: ["*"],
|
50
|
+
workerLogging: {},
|
51
|
+
schedulerLogging: {},
|
52
|
+
timeout: 100,
|
53
|
+
checkTimeout: 50,
|
54
|
+
minTaskProcessors: 1,
|
55
|
+
maxTaskProcessors: 1,
|
56
|
+
maxEventLoopDelay: 5,
|
57
|
+
stuckWorkerTimeout: 1000 * 60 * 60,
|
58
|
+
connectionOptions: {
|
59
|
+
tasks: {},
|
60
|
+
},
|
61
|
+
};
|
62
|
+
},
|
63
|
+
},
|
64
|
+
}));
|
65
|
+
|
66
|
+
const actionhero = new Process();
|
67
|
+
|
68
|
+
describe("with ioredis-mock", () => {
|
69
|
+
beforeAll(async () => {
|
70
|
+
process.env.AUTOMATIC_ROUTES = "get";
|
71
|
+
await actionhero.start();
|
72
|
+
});
|
73
|
+
|
74
|
+
afterAll(async () => await actionhero.stop());
|
75
|
+
|
76
|
+
test("basic redis operations work and data is shared between the connections", async () => {
|
77
|
+
await api.redis.clients.client.set("__test", "abc");
|
78
|
+
expect(await api.redis.clients.client.get("__test")).toBe("abc");
|
79
|
+
expect(await api.redis.clients.tasks.get("__test")).toBe("abc");
|
80
|
+
});
|
81
|
+
|
82
|
+
test("redis pub-sub works between connections", async () => {
|
83
|
+
let message: string;
|
84
|
+
api.redis.clients.subscriber.subscribe("test-channel");
|
85
|
+
const subscription = api.redis.clients.subscriber.on(
|
86
|
+
"message",
|
87
|
+
(channel, m) => {
|
88
|
+
if (channel === "test-channel") message = m;
|
89
|
+
},
|
90
|
+
);
|
91
|
+
|
92
|
+
await api.redis.clients.client.publish("test-channel", "hello");
|
93
|
+
await utils.sleep(10);
|
94
|
+
|
95
|
+
//@ts-ignore
|
96
|
+
expect(message).toBe("hello");
|
97
|
+
|
98
|
+
api.redis.clients.subscriber.unsubscribe("test-channel");
|
99
|
+
});
|
100
|
+
|
101
|
+
test("chat works", async () => {
|
102
|
+
try {
|
103
|
+
await chatRoom.add("defaultRoom");
|
104
|
+
} catch {}
|
105
|
+
|
106
|
+
const client1 = await specHelper.buildConnection();
|
107
|
+
const client2 = await specHelper.buildConnection();
|
108
|
+
client1.verbs("roomAdd", "defaultRoom");
|
109
|
+
client2.verbs("roomAdd", "defaultRoom");
|
110
|
+
|
111
|
+
await utils.sleep(10);
|
112
|
+
await client1.verbs("say", ["defaultRoom", "Hi"]);
|
113
|
+
await utils.sleep(10);
|
114
|
+
|
115
|
+
if (!client2.messages) throw new Error("client2 has no messages");
|
116
|
+
const { message, room, from } =
|
117
|
+
client2.messages[client2.messages.length - 1];
|
118
|
+
|
119
|
+
expect(message).toEqual("Hi");
|
120
|
+
expect(room).toEqual("defaultRoom");
|
121
|
+
expect(from).toEqual(client1.id);
|
122
|
+
});
|
123
|
+
|
124
|
+
test("cache works", async () => {
|
125
|
+
await cache.save("testKey", { test: "value" });
|
126
|
+
const { key, value } = await cache.load("testKey");
|
127
|
+
expect(key).toBe("testKey");
|
128
|
+
expect(value).toEqual({ test: "value" });
|
129
|
+
});
|
130
|
+
|
131
|
+
describe("tasks", () => {
|
132
|
+
let taskOutput: string[] = [];
|
133
|
+
|
134
|
+
beforeAll(async () => {
|
135
|
+
class RegularTask extends Task {
|
136
|
+
constructor() {
|
137
|
+
super();
|
138
|
+
this.name = "regular";
|
139
|
+
this.description = "task: regular";
|
140
|
+
this.queue = "testQueue";
|
141
|
+
this.frequency = 0;
|
142
|
+
}
|
143
|
+
|
144
|
+
async run(params: { word: string }) {
|
145
|
+
taskOutput.push(params.word);
|
146
|
+
return params.word;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
api.tasks.tasks.regularTask = new RegularTask();
|
151
|
+
api.tasks.jobs.regularTask = api.tasks.jobWrapper("regularTask");
|
152
|
+
});
|
153
|
+
|
154
|
+
beforeEach(() => {
|
155
|
+
taskOutput = [];
|
156
|
+
});
|
157
|
+
|
158
|
+
afterAll(async () => {
|
159
|
+
delete api.tasks.tasks.regularTask;
|
160
|
+
delete api.tasks.jobs.regularTask;
|
161
|
+
});
|
162
|
+
|
163
|
+
test("tasks work inline (quick)", async () => {
|
164
|
+
const response = await specHelper.runTask("regularTask", {
|
165
|
+
word: "test",
|
166
|
+
});
|
167
|
+
expect(response).toBe("test");
|
168
|
+
expect(taskOutput).toEqual(["test"]);
|
169
|
+
});
|
170
|
+
|
171
|
+
test("tasks work inline (full)", async () => {
|
172
|
+
const response = await specHelper.runFullTask("regularTask", {
|
173
|
+
word: "test",
|
174
|
+
});
|
175
|
+
expect(response).toBe("test");
|
176
|
+
expect(taskOutput).toEqual(["test"]);
|
177
|
+
});
|
178
|
+
|
179
|
+
test("workers and scheduler work", async () => {
|
180
|
+
await task.enqueueIn(1, "regularTask", { word: "test-full" });
|
181
|
+
|
182
|
+
const jobs = await task.allDelayed();
|
183
|
+
const times = Object.keys(jobs);
|
184
|
+
expect(jobs[times[0]][0]).toEqual({
|
185
|
+
args: [{ word: "test-full" }],
|
186
|
+
class: "regularTask",
|
187
|
+
queue: "testQueue",
|
188
|
+
});
|
189
|
+
|
190
|
+
await utils.sleep(1500);
|
191
|
+
expect(taskOutput).toEqual(["test-full"]);
|
192
|
+
}, 10000);
|
193
|
+
});
|
194
|
+
});
|
@@ -0,0 +1,97 @@
|
|
1
|
+
process.env.AUTOMATIC_ROUTES = "get";
|
2
|
+
|
3
|
+
import axios from "axios";
|
4
|
+
import * as stream from "stream";
|
5
|
+
import { api, Process, config } from "./../../src/index";
|
6
|
+
|
7
|
+
const actionhero = new Process();
|
8
|
+
let url: string;
|
9
|
+
|
10
|
+
describe("Server: sendBuffer", () => {
|
11
|
+
beforeAll(async () => {
|
12
|
+
await actionhero.start();
|
13
|
+
url = "http://localhost:" + config.web!.port;
|
14
|
+
});
|
15
|
+
|
16
|
+
afterAll(async () => await actionhero.stop());
|
17
|
+
|
18
|
+
beforeAll(() => {
|
19
|
+
api.actions.versions.sendBufferTest = [1];
|
20
|
+
api.actions.actions.sendBufferTest = {
|
21
|
+
// @ts-ignore
|
22
|
+
1: {
|
23
|
+
name: "sendBufferTest",
|
24
|
+
description: "sendBufferTest",
|
25
|
+
version: 1,
|
26
|
+
run: async (data) => {
|
27
|
+
const buffer = "Example of data buffer";
|
28
|
+
const bufferStream = new stream.PassThrough();
|
29
|
+
data.connection!.rawConnection.responseHeaders.push([
|
30
|
+
"Content-Disposition",
|
31
|
+
"attachment; filename=test.csv",
|
32
|
+
]);
|
33
|
+
api.servers.servers.web.sendFile(
|
34
|
+
data.connection!,
|
35
|
+
// @ts-ignore
|
36
|
+
null,
|
37
|
+
bufferStream,
|
38
|
+
"text/csv",
|
39
|
+
buffer.length,
|
40
|
+
new Date(),
|
41
|
+
);
|
42
|
+
data.toRender = false;
|
43
|
+
bufferStream.end(buffer);
|
44
|
+
},
|
45
|
+
},
|
46
|
+
};
|
47
|
+
|
48
|
+
api.actions.versions.sendUnknownLengthBufferTest = [1];
|
49
|
+
api.actions.actions.sendUnknownLengthBufferTest = {
|
50
|
+
// @ts-ignore
|
51
|
+
1: {
|
52
|
+
name: "sendUnknownLengthBufferTest",
|
53
|
+
description: "sendUnknownLengthBufferTest",
|
54
|
+
version: 1,
|
55
|
+
run: async (data) => {
|
56
|
+
const bufferStream = new stream.PassThrough();
|
57
|
+
api.servers.servers.web.sendFile(
|
58
|
+
data.connection!,
|
59
|
+
// @ts-ignore
|
60
|
+
null,
|
61
|
+
bufferStream,
|
62
|
+
"text/plain",
|
63
|
+
null,
|
64
|
+
new Date(),
|
65
|
+
);
|
66
|
+
const buffer = "Example of unknown length data buffer";
|
67
|
+
data.toRender = false;
|
68
|
+
bufferStream.end(buffer);
|
69
|
+
},
|
70
|
+
},
|
71
|
+
};
|
72
|
+
|
73
|
+
api.routes.loadRoutes();
|
74
|
+
});
|
75
|
+
|
76
|
+
afterAll(() => {
|
77
|
+
delete api.actions.actions.sendBufferTest;
|
78
|
+
delete api.actions.versions.sendBufferTest;
|
79
|
+
delete api.actions.versions.sendUnknownLengthBufferTest;
|
80
|
+
delete api.actions.versions.sendUnknownLengthBufferTest;
|
81
|
+
api.routes.loadRoutes();
|
82
|
+
});
|
83
|
+
|
84
|
+
test("Server should sendBuffer", async () => {
|
85
|
+
const response = await axios.get(url + "/api/sendBufferTest");
|
86
|
+
expect(response.data).toEqual("Example of data buffer");
|
87
|
+
});
|
88
|
+
|
89
|
+
test("Server should send a stream with no specified length", async () => {
|
90
|
+
const { data, headers } = await axios.get(
|
91
|
+
url + "/api/sendUnknownLengthBufferTest",
|
92
|
+
);
|
93
|
+
|
94
|
+
expect(headers).not.toHaveProperty("content-length");
|
95
|
+
expect(data).toEqual("Example of unknown length data buffer");
|
96
|
+
});
|
97
|
+
});
|
@@ -0,0 +1,24 @@
|
|
1
|
+
process.env.AUTOMATIC_ROUTES = "get";
|
2
|
+
|
3
|
+
import axios from "axios";
|
4
|
+
import * as fs from "fs";
|
5
|
+
import { Process, config } from "./../../src/index";
|
6
|
+
|
7
|
+
const actionhero = new Process();
|
8
|
+
let url: string;
|
9
|
+
|
10
|
+
describe("Server: sendFile", () => {
|
11
|
+
beforeAll(async () => {
|
12
|
+
await actionhero.start();
|
13
|
+
url = "http://localhost:" + config.web!.port;
|
14
|
+
});
|
15
|
+
|
16
|
+
afterAll(async () => await actionhero.stop());
|
17
|
+
|
18
|
+
test("Server should sendFile", async () => {
|
19
|
+
const stats = fs.statSync(__dirname + "/../../public/logo/actionhero.png");
|
20
|
+
const response = await axios.get(url + "/api/sendFile");
|
21
|
+
expect(stats.size).toBeGreaterThanOrEqual(response.data.length);
|
22
|
+
expect(response.data).toContain("PNG");
|
23
|
+
});
|
24
|
+
});
|
@@ -0,0 +1,82 @@
|
|
1
|
+
const window = { location: {} };
|
2
|
+
|
3
|
+
process.env.AUTOMATIC_ROUTES = "get";
|
4
|
+
|
5
|
+
import * as _Primus from "primus";
|
6
|
+
import axios from "axios";
|
7
|
+
import { api, Process, config } from "./../../src/index";
|
8
|
+
|
9
|
+
const actionhero = new Process();
|
10
|
+
let ActionheroWebsocketClient: any;
|
11
|
+
let fingerprint: string;
|
12
|
+
let url: string;
|
13
|
+
|
14
|
+
const connectClient = async (query = ""): Promise<any> => {
|
15
|
+
const S = _Primus.createSocket(undefined);
|
16
|
+
const clientSocket = new S(`http://localhost:${config.web!.port}?${query}`);
|
17
|
+
|
18
|
+
let client = new ActionheroWebsocketClient({}, clientSocket); // eslint-disable-line
|
19
|
+
const connectResponse = await new Promise((resolve, reject) => {
|
20
|
+
client.connect(
|
21
|
+
(error: NodeJS.ErrnoException, connectResponse: Record<string, any>) => {
|
22
|
+
if (error) {
|
23
|
+
return reject(error);
|
24
|
+
}
|
25
|
+
resolve(connectResponse);
|
26
|
+
},
|
27
|
+
);
|
28
|
+
});
|
29
|
+
|
30
|
+
return { client, connectResponse };
|
31
|
+
};
|
32
|
+
|
33
|
+
describe("Integration: Web Server + Websocket Socket shared fingerprint", () => {
|
34
|
+
beforeAll(async () => {
|
35
|
+
await actionhero.start();
|
36
|
+
await api.redis.clients.client.flushdb();
|
37
|
+
url = "http://localhost:" + config.web!.port;
|
38
|
+
ActionheroWebsocketClient = eval(
|
39
|
+
// @ts-ignore
|
40
|
+
api.servers.servers.websocket.compileActionheroWebsocketClientJS(),
|
41
|
+
); // eslint-disable-line
|
42
|
+
});
|
43
|
+
|
44
|
+
afterAll(async () => await actionhero.stop());
|
45
|
+
|
46
|
+
test("should exist when web server been called", async () => {
|
47
|
+
const response = await axios.get(url + "/api/randomNumber");
|
48
|
+
fingerprint = response.data.requesterInformation.fingerprint;
|
49
|
+
const query = `${
|
50
|
+
(config.web!.fingerprintOptions as Record<string, any>).cookieKey
|
51
|
+
}=${fingerprint}`;
|
52
|
+
const { client, connectResponse } = await connectClient(query);
|
53
|
+
expect(connectResponse.status).toEqual("OK");
|
54
|
+
expect(connectResponse.data.id).toBeTruthy();
|
55
|
+
const id = connectResponse.data.id;
|
56
|
+
expect(api.connections.connections[id].fingerprint).toEqual(fingerprint);
|
57
|
+
client.disconnect();
|
58
|
+
});
|
59
|
+
|
60
|
+
test("should not exist when web server has not been called", async () => {
|
61
|
+
const { client, connectResponse } = await connectClient();
|
62
|
+
expect(connectResponse.status).toEqual("OK");
|
63
|
+
expect(connectResponse.data.id).toBeTruthy();
|
64
|
+
const id = connectResponse.data.id;
|
65
|
+
expect(api.connections.connections[id].fingerprint).not.toEqual(
|
66
|
+
fingerprint,
|
67
|
+
);
|
68
|
+
client.disconnect();
|
69
|
+
});
|
70
|
+
|
71
|
+
test("should exist as long as cookie is passed", async () => {
|
72
|
+
const query = `${
|
73
|
+
(config.web!.fingerprintOptions as Record<string, any>).cookieKey
|
74
|
+
}=dummyValue`;
|
75
|
+
const { client, connectResponse } = await connectClient(query);
|
76
|
+
expect(connectResponse.status).toEqual("OK");
|
77
|
+
expect(connectResponse.data.id).toBeTruthy();
|
78
|
+
const id = connectResponse.data.id;
|
79
|
+
expect(api.connections.connections[id].fingerprint).toEqual("dummyValue");
|
80
|
+
client.disconnect();
|
81
|
+
});
|
82
|
+
});
|
@@ -0,0 +1,110 @@
|
|
1
|
+
jest.mock("./../../src/config/tasks.ts", () => ({
|
2
|
+
__esModule: true,
|
3
|
+
test: {
|
4
|
+
tasks: () => {
|
5
|
+
return {
|
6
|
+
_toExpand: false,
|
7
|
+
scheduler: true,
|
8
|
+
queues: ["*"],
|
9
|
+
workerLogging: {
|
10
|
+
failure: "error", // task failure
|
11
|
+
success: "info", // task success
|
12
|
+
start: "info",
|
13
|
+
end: "info",
|
14
|
+
cleaning_worker: "info",
|
15
|
+
poll: "debug",
|
16
|
+
job: "debug",
|
17
|
+
pause: "debug",
|
18
|
+
internalError: "error",
|
19
|
+
multiWorkerAction: "debug",
|
20
|
+
},
|
21
|
+
schedulerLogging: {
|
22
|
+
start: "info",
|
23
|
+
end: "info",
|
24
|
+
poll: "debug",
|
25
|
+
enqueue: "debug",
|
26
|
+
reEnqueue: "debug",
|
27
|
+
working_timestamp: "debug",
|
28
|
+
transferred_job: "debug",
|
29
|
+
},
|
30
|
+
timeout: 500,
|
31
|
+
minTaskProcessors: 1,
|
32
|
+
maxTaskProcessors: 1,
|
33
|
+
checkTimeout: 500,
|
34
|
+
maxEventLoopDelay: 5,
|
35
|
+
stuckWorkerTimeout: 1000 * 60 * 60,
|
36
|
+
resque_overrides: {},
|
37
|
+
connectionOptions: {
|
38
|
+
tasks: {},
|
39
|
+
},
|
40
|
+
};
|
41
|
+
},
|
42
|
+
},
|
43
|
+
}));
|
44
|
+
|
45
|
+
import * as path from "path";
|
46
|
+
import * as fs from "fs";
|
47
|
+
import { api, Process, utils } from "../../src";
|
48
|
+
|
49
|
+
const testTaskPath = path.join(__dirname, "./../../src/tasks/test-task.ts");
|
50
|
+
fs.writeFileSync(
|
51
|
+
testTaskPath,
|
52
|
+
`
|
53
|
+
import {Task} from './../index'
|
54
|
+
|
55
|
+
export default class MyTask extends Task {
|
56
|
+
constructor() {
|
57
|
+
super();
|
58
|
+
this.name = "test-task";
|
59
|
+
this.description = "a test";
|
60
|
+
this.frequency = 1;
|
61
|
+
this.queue = "default";
|
62
|
+
this.middleware = [];
|
63
|
+
}
|
64
|
+
|
65
|
+
async run() {
|
66
|
+
process.env.DID_TASK_RUN = "yes";
|
67
|
+
}
|
68
|
+
}
|
69
|
+
`,
|
70
|
+
);
|
71
|
+
|
72
|
+
describe("task integration tests", () => {
|
73
|
+
beforeAll(() => {
|
74
|
+
process.env.DID_TASK_RUN = "no";
|
75
|
+
});
|
76
|
+
|
77
|
+
test("the variable started as no", () => {
|
78
|
+
expect(process.env.DID_TASK_RUN).toBe("no");
|
79
|
+
});
|
80
|
+
|
81
|
+
describe("with a running server", () => {
|
82
|
+
let actionhero: Process;
|
83
|
+
|
84
|
+
beforeAll(async () => {
|
85
|
+
actionhero = new Process();
|
86
|
+
await actionhero.start();
|
87
|
+
await api.redis.clients.client.set("resque:stat:processed", 0);
|
88
|
+
});
|
89
|
+
|
90
|
+
afterAll(async () => {
|
91
|
+
await actionhero.stop();
|
92
|
+
fs.unlinkSync(testTaskPath);
|
93
|
+
});
|
94
|
+
|
95
|
+
test("the periodic task should have started", async () => {
|
96
|
+
const getCount = async () => {
|
97
|
+
const resp = await api.redis.clients.client.get(
|
98
|
+
"resque:stat:processed",
|
99
|
+
);
|
100
|
+
if (!resp) throw new Error("no response from redis");
|
101
|
+
return parseInt(resp);
|
102
|
+
};
|
103
|
+
|
104
|
+
while ((await getCount()) < 1) await utils.sleep(100);
|
105
|
+
|
106
|
+
expect(await getCount()).toBeGreaterThan(0);
|
107
|
+
expect(process.env.DID_TASK_RUN).toBe("yes");
|
108
|
+
});
|
109
|
+
});
|
110
|
+
});
|