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,42 @@
|
|
1
|
+
import { Process, api } from "./../../src";
|
2
|
+
|
3
|
+
const actionhero = new Process();
|
4
|
+
|
5
|
+
describe("Core", () => {
|
6
|
+
describe("Process", () => {
|
7
|
+
test("a new process can be created", () => {
|
8
|
+
expect(actionhero.initialized).toBe(false);
|
9
|
+
expect(actionhero.started).toBe(false);
|
10
|
+
expect(actionhero.stopped).toBe(false);
|
11
|
+
});
|
12
|
+
|
13
|
+
test("a new process can be initialized", async () => {
|
14
|
+
await actionhero.initialize();
|
15
|
+
expect(actionhero.initialized).toBe(true);
|
16
|
+
expect(actionhero.started).toBe(false);
|
17
|
+
expect(actionhero.stopped).toBe(false);
|
18
|
+
});
|
19
|
+
|
20
|
+
test("a new process can be started", async () => {
|
21
|
+
await actionhero.start();
|
22
|
+
expect(actionhero.initialized).toBe(true);
|
23
|
+
expect(actionhero.started).toBe(true);
|
24
|
+
expect(actionhero.stopped).toBe(false);
|
25
|
+
});
|
26
|
+
|
27
|
+
test("a new process can be stopped", async () => {
|
28
|
+
await actionhero.stop("some reason");
|
29
|
+
expect(actionhero.stopReasons).toEqual(["some reason"]);
|
30
|
+
expect(actionhero.initialized).toBe(false);
|
31
|
+
expect(actionhero.started).toBe(false);
|
32
|
+
expect(actionhero.stopped).toBe(true);
|
33
|
+
});
|
34
|
+
|
35
|
+
test("the process is injected into the global API import", () => {
|
36
|
+
//@ts-ignore
|
37
|
+
actionhero["testProperty"] = { a: 1 };
|
38
|
+
//@ts-ignore
|
39
|
+
expect(api.process["testProperty"]).toEqual({ a: 1 });
|
40
|
+
});
|
41
|
+
});
|
42
|
+
});
|
@@ -0,0 +1,330 @@
|
|
1
|
+
import { api, Connection, Process, specHelper, task } from "./../../src";
|
2
|
+
import { RandomNumber } from "../../src/actions/randomNumber";
|
3
|
+
import { SleepTest } from "../../src/actions/sleepTest";
|
4
|
+
|
5
|
+
const actionhero = new Process();
|
6
|
+
|
7
|
+
describe("Core: specHelper", () => {
|
8
|
+
beforeAll(async () => await actionhero.start());
|
9
|
+
afterAll(async () => await actionhero.stop());
|
10
|
+
|
11
|
+
test("can make a request with just params", async () => {
|
12
|
+
const { randomNumber } =
|
13
|
+
await specHelper.runAction<RandomNumber>("randomNumber");
|
14
|
+
expect(randomNumber).toBeGreaterThanOrEqual(0);
|
15
|
+
expect(randomNumber).toBeLessThan(1);
|
16
|
+
});
|
17
|
+
|
18
|
+
test("will stack up messages received", async () => {
|
19
|
+
const connection = await specHelper.buildConnection();
|
20
|
+
connection.params.thing = "stuff";
|
21
|
+
const { error } = await specHelper.runAction("x", connection);
|
22
|
+
if (!connection.messages) throw new Error("no connection.messages");
|
23
|
+
expect(connection.messages).toHaveLength(2);
|
24
|
+
expect(connection.messages[0].welcome).toEqual(
|
25
|
+
"Welcome to the actionhero api",
|
26
|
+
);
|
27
|
+
expect(connection.messages[1].error).toEqual(
|
28
|
+
"Error: unknown action or invalid apiVersion",
|
29
|
+
);
|
30
|
+
expect(error).toEqual("Error: unknown action or invalid apiVersion");
|
31
|
+
});
|
32
|
+
|
33
|
+
describe("metadata, type-safety, and errors", () => {
|
34
|
+
beforeAll(() => {
|
35
|
+
api.actions.versions.stringResponseTestAction = [1];
|
36
|
+
api.actions.actions.stringResponseTestAction = {
|
37
|
+
//@ts-ignore
|
38
|
+
1: {
|
39
|
+
name: "stringResponseTestAction",
|
40
|
+
description: "stringResponseTestAction",
|
41
|
+
version: 1,
|
42
|
+
run: async (data) => {
|
43
|
+
//@ts-ignore
|
44
|
+
data.response = "something response";
|
45
|
+
},
|
46
|
+
},
|
47
|
+
};
|
48
|
+
|
49
|
+
api.actions.versions.stringErrorTestAction = [1];
|
50
|
+
api.actions.actions.stringErrorTestAction = {
|
51
|
+
//@ts-ignore
|
52
|
+
1: {
|
53
|
+
name: "stringErrorTestAction",
|
54
|
+
description: "stringErrorTestAction",
|
55
|
+
version: 1,
|
56
|
+
run: (data) => {
|
57
|
+
//@ts-ignore
|
58
|
+
data.response = "something response";
|
59
|
+
throw new Error("some error");
|
60
|
+
},
|
61
|
+
},
|
62
|
+
};
|
63
|
+
|
64
|
+
api.actions.versions.arrayResponseTestAction = [1];
|
65
|
+
api.actions.actions.arrayResponseTestAction = {
|
66
|
+
//@ts-ignore
|
67
|
+
1: {
|
68
|
+
name: "arrayResponseTestAction",
|
69
|
+
description: "arrayResponseTestAction",
|
70
|
+
version: 1,
|
71
|
+
run: async (data) => {
|
72
|
+
data.response = [1, 2, 3];
|
73
|
+
},
|
74
|
+
},
|
75
|
+
};
|
76
|
+
|
77
|
+
api.actions.versions.arrayErrorTestAction = [1];
|
78
|
+
api.actions.actions.arrayErrorTestAction = {
|
79
|
+
//@ts-ignore
|
80
|
+
1: {
|
81
|
+
name: "arrayErrorTestAction",
|
82
|
+
description: "arrayErrorTestAction",
|
83
|
+
version: 1,
|
84
|
+
run: (data) => {
|
85
|
+
data.response = [1, 2, 3];
|
86
|
+
throw new Error("some error");
|
87
|
+
},
|
88
|
+
},
|
89
|
+
};
|
90
|
+
});
|
91
|
+
|
92
|
+
afterAll(() => {
|
93
|
+
delete api.actions.actions.stringResponseTestAction;
|
94
|
+
delete api.actions.versions.stringResponseTestAction;
|
95
|
+
delete api.actions.actions.stringErrorTestAction;
|
96
|
+
delete api.actions.versions.stringErrorTestAction;
|
97
|
+
delete api.actions.actions.arrayResponseTestAction;
|
98
|
+
delete api.actions.versions.arrayResponseTestAction;
|
99
|
+
delete api.actions.actions.arrayErrorTestAction;
|
100
|
+
delete api.actions.versions.arrayErrorTestAction;
|
101
|
+
});
|
102
|
+
|
103
|
+
describe("happy-path", () => {
|
104
|
+
test("if the response payload is an object, it appends metadata", async () => {
|
105
|
+
const response =
|
106
|
+
await specHelper.runAction<RandomNumber>("randomNumber");
|
107
|
+
expect(response.error).toBeUndefined();
|
108
|
+
expect(response.randomNumber).toBeTruthy();
|
109
|
+
expect(response.messageId).toBeTruthy();
|
110
|
+
expect(response.serverInformation!.serverName).toEqual("actionhero");
|
111
|
+
expect(response.requesterInformation!.remoteIP).toEqual("testServer");
|
112
|
+
});
|
113
|
+
|
114
|
+
test("if the response payload is a string, it maintains type", async () => {
|
115
|
+
const response = await specHelper.runAction("stringResponseTestAction");
|
116
|
+
expect(response).toEqual("something response");
|
117
|
+
expect(response.error).toBeUndefined();
|
118
|
+
expect(response.messageId).toBeUndefined();
|
119
|
+
expect(response.serverInformation).toBeUndefined();
|
120
|
+
expect(response.requesterInformation).toBeUndefined();
|
121
|
+
});
|
122
|
+
|
123
|
+
test("if the response payload is a array, it maintains type", async () => {
|
124
|
+
const response = await specHelper.runAction("arrayResponseTestAction");
|
125
|
+
expect(response).toEqual([1, 2, 3]);
|
126
|
+
expect(response.error).toBeUndefined();
|
127
|
+
expect(response.messageId).toBeUndefined();
|
128
|
+
expect(response.serverInformation).toBeUndefined();
|
129
|
+
expect(response.requesterInformation).toBeUndefined();
|
130
|
+
});
|
131
|
+
});
|
132
|
+
|
133
|
+
describe("disabling metadata", () => {
|
134
|
+
beforeAll(() => {
|
135
|
+
api.specHelper.returnMetadata = false;
|
136
|
+
});
|
137
|
+
afterAll(() => {
|
138
|
+
api.specHelper.returnMetadata = true;
|
139
|
+
});
|
140
|
+
|
141
|
+
test("if the response payload is an object, it should not append metadata", async () => {
|
142
|
+
const response =
|
143
|
+
await specHelper.runAction<RandomNumber>("randomNumber");
|
144
|
+
expect(response.error).toBeUndefined();
|
145
|
+
expect(response.randomNumber).toBeTruthy();
|
146
|
+
expect(response.messageId).toBeUndefined();
|
147
|
+
expect(response.serverInformation).toBeUndefined();
|
148
|
+
expect(response.requesterInformation).toBeUndefined();
|
149
|
+
});
|
150
|
+
});
|
151
|
+
|
152
|
+
describe("errors", () => {
|
153
|
+
test("if the response payload is an object and there is an error, it appends metadata", async () => {
|
154
|
+
const response = await specHelper.runAction("x");
|
155
|
+
expect(response.error).toEqual(
|
156
|
+
"Error: unknown action or invalid apiVersion",
|
157
|
+
);
|
158
|
+
expect(response.messageId).toBeTruthy();
|
159
|
+
expect(response.serverInformation!.serverName).toEqual("actionhero");
|
160
|
+
expect(response.requesterInformation!.remoteIP).toEqual("testServer");
|
161
|
+
});
|
162
|
+
|
163
|
+
test("if the response payload is a string, just the error will be returned", async () => {
|
164
|
+
const response = await specHelper.runAction("stringErrorTestAction");
|
165
|
+
expect(response).toEqual("Error: some error");
|
166
|
+
expect(response.messageId).toBeUndefined();
|
167
|
+
expect(response.serverInformation).toBeUndefined();
|
168
|
+
expect(response.requesterInformation).toBeUndefined();
|
169
|
+
});
|
170
|
+
|
171
|
+
test("if the response payload is a array, just the error will be returned", async () => {
|
172
|
+
const response = await specHelper.runAction("arrayErrorTestAction");
|
173
|
+
expect(response).toEqual("Error: some error");
|
174
|
+
expect(response.messageId).toBeUndefined();
|
175
|
+
expect(response.serverInformation).toBeUndefined();
|
176
|
+
expect(response.requesterInformation).toBeUndefined();
|
177
|
+
});
|
178
|
+
});
|
179
|
+
});
|
180
|
+
|
181
|
+
describe("test responses", () => {
|
182
|
+
test("will not report a broken test as a broken action (sync)", async () => {
|
183
|
+
const response = await specHelper.runAction<RandomNumber>("randomNumber");
|
184
|
+
try {
|
185
|
+
//@ts-ignore
|
186
|
+
response.not.a.real.thing();
|
187
|
+
throw new Error("should not get here");
|
188
|
+
} catch (e) {
|
189
|
+
expect(String(e)).toContain("TypeError: Cannot read");
|
190
|
+
}
|
191
|
+
});
|
192
|
+
|
193
|
+
test("will not report a broken test as a broken action (async)", async () => {
|
194
|
+
const response = await specHelper.runAction<SleepTest>("sleepTest");
|
195
|
+
try {
|
196
|
+
//@ts-ignore
|
197
|
+
response.not.a.real.thing();
|
198
|
+
throw new Error("should not get here");
|
199
|
+
} catch (e) {
|
200
|
+
expect(String(e)).toContain("TypeError: Cannot read");
|
201
|
+
}
|
202
|
+
});
|
203
|
+
|
204
|
+
test("messageId can be configurable", async () => {
|
205
|
+
const response = await specHelper.runAction("randomNumber", {
|
206
|
+
messageId: "aaa",
|
207
|
+
});
|
208
|
+
expect(response.messageId).toEqual("aaa");
|
209
|
+
});
|
210
|
+
});
|
211
|
+
|
212
|
+
describe("files", () => {
|
213
|
+
test("can request file data", async () => {
|
214
|
+
const data = await specHelper.getStaticFile("simple.html");
|
215
|
+
expect(data.error).toBeUndefined();
|
216
|
+
expect(data.content).toContain("<h1>Actionhero</h1>");
|
217
|
+
expect(data.mime).toEqual("text/html");
|
218
|
+
expect(data.length).toEqual(101);
|
219
|
+
});
|
220
|
+
|
221
|
+
test("missing files", async () => {
|
222
|
+
const data = await specHelper.getStaticFile("missing.html");
|
223
|
+
expect(data.error).toEqual("that file is not found");
|
224
|
+
expect(data.mime).toEqual("text/html");
|
225
|
+
expect(data.content).toBeNull();
|
226
|
+
});
|
227
|
+
});
|
228
|
+
|
229
|
+
describe("persistent test connections", () => {
|
230
|
+
let connection: Connection;
|
231
|
+
let connId: string;
|
232
|
+
const messageIds: string[] = [];
|
233
|
+
|
234
|
+
test("can make a request with a specified connection", async () => {
|
235
|
+
connection = await specHelper.buildConnection();
|
236
|
+
connection.params = {
|
237
|
+
key: "someKey",
|
238
|
+
value: "someValue",
|
239
|
+
};
|
240
|
+
|
241
|
+
connId = connection.id;
|
242
|
+
|
243
|
+
const response = await specHelper.runAction("cacheTest", connection);
|
244
|
+
messageIds.push(response.messageId!);
|
245
|
+
expect(connection.messages).toHaveLength(2);
|
246
|
+
expect(connId).toEqual(connection.id);
|
247
|
+
expect(connection.fingerprint).toEqual(connId);
|
248
|
+
});
|
249
|
+
|
250
|
+
test("can make second request", async () => {
|
251
|
+
const response = await specHelper.runAction("randomNumber", connection);
|
252
|
+
messageIds.push(response.messageId!);
|
253
|
+
expect(connection.messages).toHaveLength(3);
|
254
|
+
expect(connId).toEqual(connection.id);
|
255
|
+
expect(connection.fingerprint).toEqual(connId);
|
256
|
+
});
|
257
|
+
|
258
|
+
test("will generate new ids and fingerprints for a new connection", async () => {
|
259
|
+
const response = await specHelper.runAction("randomNumber");
|
260
|
+
messageIds.push(response.messageId!);
|
261
|
+
expect(response.requesterInformation!.id).not.toEqual(connId);
|
262
|
+
expect(response.requesterInformation!.fingerprint).not.toEqual(connId);
|
263
|
+
});
|
264
|
+
|
265
|
+
test("message ids are unique", () => {
|
266
|
+
expect(messageIds).toHaveLength(3);
|
267
|
+
expect(messageIds[0]).not.toEqual(messageIds[1]);
|
268
|
+
expect(messageIds[1]).not.toEqual(messageIds[2]);
|
269
|
+
});
|
270
|
+
});
|
271
|
+
|
272
|
+
describe("tasks", () => {
|
273
|
+
let taskRan = false;
|
274
|
+
beforeAll(() => {
|
275
|
+
api.tasks.tasks.testTask = {
|
276
|
+
name: "testTask",
|
277
|
+
description: "task: test: " + Math.random(),
|
278
|
+
queue: "default",
|
279
|
+
frequency: 0,
|
280
|
+
plugins: [],
|
281
|
+
pluginOptions: {},
|
282
|
+
run: async () => {
|
283
|
+
taskRan = true;
|
284
|
+
return "OK";
|
285
|
+
},
|
286
|
+
};
|
287
|
+
|
288
|
+
api.tasks.jobs.testTask = api.tasks.jobWrapper("testTask");
|
289
|
+
});
|
290
|
+
|
291
|
+
afterAll(() => {
|
292
|
+
delete api.testOutput;
|
293
|
+
delete api.tasks.tasks.testTask;
|
294
|
+
});
|
295
|
+
|
296
|
+
test("can run tasks", async () => {
|
297
|
+
const response = await specHelper.runTask("testTask", {});
|
298
|
+
expect(response).toEqual("OK");
|
299
|
+
expect(taskRan).toEqual(true);
|
300
|
+
});
|
301
|
+
|
302
|
+
describe("flushed redis", () => {
|
303
|
+
beforeEach(async () => {
|
304
|
+
await api.redis.clients.client.flushdb();
|
305
|
+
});
|
306
|
+
|
307
|
+
test("findEnqueuedTasks (normal queues)", async () => {
|
308
|
+
await task.enqueue("testTask", { a: 1 });
|
309
|
+
const foundTasks = await specHelper.findEnqueuedTasks("testTask");
|
310
|
+
expect(foundTasks.length).toBe(1);
|
311
|
+
expect(foundTasks[0].args[0]).toEqual({ a: 1 });
|
312
|
+
});
|
313
|
+
|
314
|
+
test("findEnqueuedTasks (delayed queues)", async () => {
|
315
|
+
await task.enqueueIn(1, "testTask", { a: 1 });
|
316
|
+
const foundTasks = await specHelper.findEnqueuedTasks("testTask");
|
317
|
+
expect(foundTasks.length).toBe(1);
|
318
|
+
expect(foundTasks[0].args[0]).toEqual({ a: 1 });
|
319
|
+
});
|
320
|
+
|
321
|
+
test("deleteEnqueuedTasks", async () => {
|
322
|
+
await task.enqueue("testTask", { a: 1 });
|
323
|
+
await task.enqueueAt(10, "testTask", { a: 1 });
|
324
|
+
await specHelper.deleteEnqueuedTasks("testTask", { a: 1 });
|
325
|
+
const foundTasks = await specHelper.findEnqueuedTasks("testTask");
|
326
|
+
expect(foundTasks.length).toBe(0);
|
327
|
+
});
|
328
|
+
});
|
329
|
+
});
|
330
|
+
});
|
@@ -0,0 +1,99 @@
|
|
1
|
+
import axios, { AxiosError } from "axios";
|
2
|
+
import { Process, config } from "./../../../src/index";
|
3
|
+
|
4
|
+
const actionhero = new Process();
|
5
|
+
let url: string;
|
6
|
+
|
7
|
+
jest.mock("./../../../src/config/web.ts", () => ({
|
8
|
+
__esModule: true,
|
9
|
+
test: {
|
10
|
+
web: () => {
|
11
|
+
return {
|
12
|
+
compress: true,
|
13
|
+
enabled: true,
|
14
|
+
secure: false,
|
15
|
+
urlPathForActions: "api",
|
16
|
+
urlPathForFiles: "public",
|
17
|
+
rootEndpointType: "file",
|
18
|
+
port: 18080 + parseInt(process.env.JEST_WORKER_ID || "0"),
|
19
|
+
matchExtensionMime: true,
|
20
|
+
metadataOptions: {
|
21
|
+
serverInformation: true,
|
22
|
+
requesterInformation: false,
|
23
|
+
},
|
24
|
+
fingerprintOptions: {
|
25
|
+
cookieKey: "sessionID",
|
26
|
+
},
|
27
|
+
};
|
28
|
+
},
|
29
|
+
},
|
30
|
+
}));
|
31
|
+
|
32
|
+
describe("Core", () => {
|
33
|
+
describe("static file", () => {
|
34
|
+
beforeAll(async () => {
|
35
|
+
await actionhero.start();
|
36
|
+
url =
|
37
|
+
"http://localhost:" +
|
38
|
+
config.web!.port +
|
39
|
+
"/" +
|
40
|
+
config.web!.urlPathForFiles;
|
41
|
+
});
|
42
|
+
|
43
|
+
afterAll(async () => {
|
44
|
+
await actionhero.stop();
|
45
|
+
});
|
46
|
+
|
47
|
+
describe("Compression", () => {
|
48
|
+
test("should respect accept-encoding header priority with gzip as first in a list of encodings", async () => {
|
49
|
+
const response = await axios.get(url + "/simple.html", {
|
50
|
+
headers: { "Accept-Encoding": "gzip, deflate, sdch, br" },
|
51
|
+
decompress: false,
|
52
|
+
});
|
53
|
+
|
54
|
+
expect(response.status).toEqual(200);
|
55
|
+
expect(response.headers["content-encoding"]).toEqual("gzip");
|
56
|
+
});
|
57
|
+
|
58
|
+
test("should respect accept-encoding header priority with deflate as second in a list of encodings", async () => {
|
59
|
+
const response = await axios.get(url + "/simple.html", {
|
60
|
+
headers: { "Accept-Encoding": "br, deflate, gzip" },
|
61
|
+
decompress: false,
|
62
|
+
});
|
63
|
+
|
64
|
+
expect(response.status).toEqual(200);
|
65
|
+
expect(response.headers["content-encoding"]).toEqual("deflate"); // br is not a currently supported encoding
|
66
|
+
});
|
67
|
+
|
68
|
+
test("should respect accept-encoding header priority with gzip as only option", async () => {
|
69
|
+
const response = await axios.get(url + "/simple.html", {
|
70
|
+
headers: { "Accept-Encoding": "gzip" },
|
71
|
+
decompress: false,
|
72
|
+
});
|
73
|
+
|
74
|
+
expect(response.status).toEqual(200);
|
75
|
+
expect(response.headers["content-encoding"]).toEqual("gzip");
|
76
|
+
});
|
77
|
+
|
78
|
+
test("should not encode content without a valid a supported value in accept-encoding header", async () => {
|
79
|
+
const response = await axios.get(url + "/simple.html", {
|
80
|
+
headers: { "Accept-Encoding": "sdch, br" },
|
81
|
+
decompress: false,
|
82
|
+
});
|
83
|
+
|
84
|
+
expect(response.status).toEqual(200);
|
85
|
+
expect(response.headers["content-encoding"]).toBeUndefined();
|
86
|
+
});
|
87
|
+
|
88
|
+
test("should not encode content without accept-encoding header", async () => {
|
89
|
+
const response = await axios.get(url + "/simple.html", {
|
90
|
+
headers: { "Accept-Encoding": "" },
|
91
|
+
decompress: false,
|
92
|
+
});
|
93
|
+
|
94
|
+
expect(response.status).toEqual(200);
|
95
|
+
expect(response.headers["content-encoding"]).toBeUndefined();
|
96
|
+
});
|
97
|
+
});
|
98
|
+
});
|
99
|
+
});
|
@@ -0,0 +1,180 @@
|
|
1
|
+
import axios, { AxiosError } from "axios";
|
2
|
+
import * as child_process from "child_process";
|
3
|
+
import { Process, config, utils, specHelper } from "../../../src/index";
|
4
|
+
|
5
|
+
const actionhero = new Process();
|
6
|
+
let url: string;
|
7
|
+
|
8
|
+
async function exec(command: string) {
|
9
|
+
return new Promise((resolve, reject) => {
|
10
|
+
child_process.exec(command, (error, stdout, stderr) => {
|
11
|
+
if (error) {
|
12
|
+
return reject(error);
|
13
|
+
}
|
14
|
+
return resolve({ stdout, stderr });
|
15
|
+
});
|
16
|
+
});
|
17
|
+
}
|
18
|
+
|
19
|
+
describe("Core", () => {
|
20
|
+
describe("static file", () => {
|
21
|
+
beforeAll(async () => {
|
22
|
+
await actionhero.start();
|
23
|
+
if (config.web) {
|
24
|
+
url =
|
25
|
+
"http://localhost:" +
|
26
|
+
config.web.port +
|
27
|
+
"/" +
|
28
|
+
config.web.urlPathForFiles;
|
29
|
+
}
|
30
|
+
});
|
31
|
+
|
32
|
+
afterAll(async () => {
|
33
|
+
await actionhero.stop();
|
34
|
+
});
|
35
|
+
|
36
|
+
test("file: an HTML file", async () => {
|
37
|
+
const response = await specHelper.getStaticFile("simple.html");
|
38
|
+
expect(response.mime).toEqual("text/html");
|
39
|
+
expect(response.content).toContain("<h1>Actionhero</h1>");
|
40
|
+
});
|
41
|
+
|
42
|
+
test("file: 404 pages", async () => {
|
43
|
+
const response = await specHelper.getStaticFile("someRandomFile");
|
44
|
+
expect(response.error).toEqual("that file is not found");
|
45
|
+
expect(response.content).toBeNull();
|
46
|
+
});
|
47
|
+
|
48
|
+
test("I should not see files outside of the public dir", async () => {
|
49
|
+
const response = await specHelper.getStaticFile("../config/config.json");
|
50
|
+
expect(response.error).toEqual("that file is not found");
|
51
|
+
expect(response.content).toBeNull();
|
52
|
+
});
|
53
|
+
|
54
|
+
test("file: sub paths should work", async () => {
|
55
|
+
const response = await specHelper.getStaticFile("logo/actionhero.png");
|
56
|
+
expect(response.mime).toEqual("image/png");
|
57
|
+
expect(response.length).toEqual(59273);
|
58
|
+
// wacky per-OS encoding issues I guess?
|
59
|
+
expect(response.content.length).toBeGreaterThanOrEqual(50000);
|
60
|
+
expect(response.content.length).toBeLessThan(60000);
|
61
|
+
});
|
62
|
+
|
63
|
+
test("should send back the cache-control header", async () => {
|
64
|
+
const response = await axios.get(url + "/simple.html");
|
65
|
+
expect(response.status).toEqual(200);
|
66
|
+
expect(response.headers["cache-control"]).toBeTruthy();
|
67
|
+
});
|
68
|
+
|
69
|
+
test("should send back the etag header", async () => {
|
70
|
+
const response = await axios.get(url + "/simple.html");
|
71
|
+
expect(response.status).toEqual(200);
|
72
|
+
expect(response.headers.etag).toBeTruthy();
|
73
|
+
});
|
74
|
+
|
75
|
+
test('should send back a 304 if the header "if-modified-since" is present and condition matches', async () => {
|
76
|
+
const response = await axios.get(url + "/simple.html");
|
77
|
+
expect(response.status).toEqual(200);
|
78
|
+
|
79
|
+
try {
|
80
|
+
await axios(url + "/simple.html", {
|
81
|
+
headers: { "If-Modified-Since": new Date().toUTCString() },
|
82
|
+
});
|
83
|
+
throw new Error("should not get here");
|
84
|
+
} catch (error) {
|
85
|
+
if (error instanceof AxiosError) {
|
86
|
+
expect(error.toString()).toMatch(/304/);
|
87
|
+
} else throw error;
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
test("should send back a 304 if the ETAG header is present", async () => {
|
92
|
+
const response = await axios.get(url + "/simple.html");
|
93
|
+
expect(response.status).toEqual(200);
|
94
|
+
expect(response.data).toContain("<h1>Actionhero</h1>");
|
95
|
+
expect(response.headers.etag).toBeTruthy();
|
96
|
+
|
97
|
+
const etag = response.headers.etag;
|
98
|
+
const options = {
|
99
|
+
headers: { "If-None-Match": etag },
|
100
|
+
resolveWithFullResponse: true,
|
101
|
+
};
|
102
|
+
|
103
|
+
try {
|
104
|
+
await axios(url + "/simple.html", options);
|
105
|
+
throw new Error("should not get here");
|
106
|
+
} catch (error) {
|
107
|
+
if (error instanceof AxiosError) {
|
108
|
+
expect(error.toString()).toMatch(/304/);
|
109
|
+
} else throw error;
|
110
|
+
}
|
111
|
+
});
|
112
|
+
|
113
|
+
test("should send a different etag for other files", async () => {
|
114
|
+
const response = await axios.get(url + "/simple.html");
|
115
|
+
expect(response.status).toEqual(200);
|
116
|
+
expect(response.headers.etag).toBeTruthy();
|
117
|
+
const etag = response.headers.etag;
|
118
|
+
|
119
|
+
const secondResponse = await axios.get(url + "/index.html");
|
120
|
+
expect(secondResponse.status).toEqual(200);
|
121
|
+
expect(secondResponse.headers.etag).toBeTruthy();
|
122
|
+
const etagTwo = secondResponse.headers.etag;
|
123
|
+
expect(etagTwo).not.toEqual(etag);
|
124
|
+
});
|
125
|
+
|
126
|
+
test('should send back the file if the header "if-modified-since" is present but condition does not match', async () => {
|
127
|
+
const response = await axios.get(url + "/simple.html");
|
128
|
+
expect(response.status).toEqual(200);
|
129
|
+
const lastModified = new Date(response.headers["last-modified"]);
|
130
|
+
const delay = 24 * 1000 * 3600;
|
131
|
+
|
132
|
+
const secondResponse = await axios(url + "/simple.html", {
|
133
|
+
headers: {
|
134
|
+
"If-Modified-Since": new Date(
|
135
|
+
lastModified.getTime() - delay,
|
136
|
+
).toUTCString(),
|
137
|
+
},
|
138
|
+
});
|
139
|
+
|
140
|
+
expect(secondResponse.status).toEqual(200);
|
141
|
+
expect(secondResponse.data.length).toBeGreaterThan(1);
|
142
|
+
});
|
143
|
+
|
144
|
+
if (process.platform === "win32") {
|
145
|
+
console.log(
|
146
|
+
"*** CANNOT RUN FILE DESCRIPTOR TESTS ON WINDOWS. Sorry. ***",
|
147
|
+
);
|
148
|
+
} else {
|
149
|
+
describe("do not leave open file descriptors ", () => {
|
150
|
+
const lsofChk = async () => {
|
151
|
+
//@ts-ignore
|
152
|
+
const { stdout } = await exec('lsof -n -P|grep "/simple.html"|wc -l');
|
153
|
+
return stdout.trim();
|
154
|
+
};
|
155
|
+
|
156
|
+
test("closes all descriptors on statusCode 200 responses", async () => {
|
157
|
+
const response = await axios.get(url + "/simple.html");
|
158
|
+
expect(response.status).toEqual(200);
|
159
|
+
await utils.sleep(100);
|
160
|
+
expect(await lsofChk()).toEqual("0");
|
161
|
+
}, 30000);
|
162
|
+
|
163
|
+
test("closes all descriptors on statusCode 304 responses", async () => {
|
164
|
+
try {
|
165
|
+
await axios.get(url + "/simple.html", {
|
166
|
+
headers: { "if-none-match": "*" },
|
167
|
+
});
|
168
|
+
throw new Error("should return 304");
|
169
|
+
} catch (error) {
|
170
|
+
if (error instanceof AxiosError) {
|
171
|
+
expect(error.response?.status).toEqual(304);
|
172
|
+
await utils.sleep(100);
|
173
|
+
expect(await lsofChk()).toEqual("0");
|
174
|
+
} else throw error;
|
175
|
+
}
|
176
|
+
}, 30000);
|
177
|
+
});
|
178
|
+
}
|
179
|
+
});
|
180
|
+
});
|