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,349 @@
|
|
1
|
+
// Note: These tests will only run on *nix operating systems
|
2
|
+
// You can use SKIP_CLI_TEST_SETUP=true to skip the setup portion of these tests if you are testing this file repeatedly
|
3
|
+
|
4
|
+
import * as fs from "fs";
|
5
|
+
import * as path from "path";
|
6
|
+
import { spawn } from "child_process";
|
7
|
+
import axios, { AxiosError } from "axios";
|
8
|
+
import { isRunning } from "../../src/modules/utils/isRunning";
|
9
|
+
import { sleep } from "../../src/modules/utils/sleep";
|
10
|
+
|
11
|
+
const testDir = path.join(process.cwd(), "tmp", "actionheroTestProject");
|
12
|
+
const binary = "./node_modules/.bin/actionhero";
|
13
|
+
|
14
|
+
console.log(`testDir: ${testDir}`);
|
15
|
+
|
16
|
+
const port = 18080 + parseInt(process.env.JEST_WORKER_ID || "0");
|
17
|
+
const host = "localhost";
|
18
|
+
let pid: number;
|
19
|
+
let AHPath: string;
|
20
|
+
|
21
|
+
class ErrorWithStd extends Error {
|
22
|
+
stderr: string;
|
23
|
+
stdout: string;
|
24
|
+
pid: number;
|
25
|
+
exitCode: number;
|
26
|
+
}
|
27
|
+
|
28
|
+
const doCommand = async (
|
29
|
+
command: string,
|
30
|
+
useCwd = true,
|
31
|
+
extraEnv = {},
|
32
|
+
): Promise<{
|
33
|
+
stderr: string;
|
34
|
+
stdout: string;
|
35
|
+
pid: number;
|
36
|
+
exitCode: number;
|
37
|
+
}> => {
|
38
|
+
return new Promise((resolve, reject) => {
|
39
|
+
const parts = command.split(" ");
|
40
|
+
const bin = parts.shift() as string;
|
41
|
+
const args = parts;
|
42
|
+
let stdout = "";
|
43
|
+
let stderr = "";
|
44
|
+
|
45
|
+
// we don't want the CLI commands to source typescript files
|
46
|
+
// when running jest, it will reset NODE_ENV=test
|
47
|
+
// but sometimes we do /shrug/
|
48
|
+
const env = Object.assign({ ...process.env }, extraEnv);
|
49
|
+
delete env.NODE_ENV;
|
50
|
+
delete env.TS_JEST;
|
51
|
+
|
52
|
+
const cmd = spawn(bin, args, {
|
53
|
+
cwd: useCwd ? testDir : __dirname,
|
54
|
+
env: env,
|
55
|
+
});
|
56
|
+
|
57
|
+
cmd.stdout.on("data", (data) => {
|
58
|
+
stdout += data.toString();
|
59
|
+
});
|
60
|
+
cmd.stderr.on("data", (data) => {
|
61
|
+
stderr += data.toString();
|
62
|
+
});
|
63
|
+
|
64
|
+
pid = cmd.pid ?? -1;
|
65
|
+
|
66
|
+
cmd.on("close", (exitCode) => {
|
67
|
+
if (stderr.length > 0 || exitCode !== 0) {
|
68
|
+
const error = new ErrorWithStd(stderr);
|
69
|
+
error.stderr = stderr;
|
70
|
+
error.stdout = stdout;
|
71
|
+
error.pid = pid;
|
72
|
+
error.exitCode = exitCode ?? -1;
|
73
|
+
return reject(error);
|
74
|
+
}
|
75
|
+
return resolve({ stderr, stdout, pid, exitCode });
|
76
|
+
});
|
77
|
+
});
|
78
|
+
};
|
79
|
+
|
80
|
+
describe("Core: CLI", () => {
|
81
|
+
if (process.platform === "win32") {
|
82
|
+
console.log("*** CANNOT RUN CLI TESTS ON WINDOWS. Sorry. ***");
|
83
|
+
} else {
|
84
|
+
beforeAll(async () => {
|
85
|
+
if (process.env.SKIP_CLI_TEST_SETUP === "true") {
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
89
|
+
const sourcePackage = path.normalize(
|
90
|
+
path.join(__dirname, "/../../templates/package.json.template"),
|
91
|
+
);
|
92
|
+
AHPath = path.normalize(path.join(__dirname, "/../.."));
|
93
|
+
|
94
|
+
await doCommand(`rm -rf ${testDir}`, false);
|
95
|
+
await doCommand(`mkdir -p ${testDir}`, false);
|
96
|
+
await doCommand(`cp ${sourcePackage} ${testDir}/package.json`);
|
97
|
+
|
98
|
+
const data = fs.readFileSync(testDir + "/package.json").toString();
|
99
|
+
const result = data.replace(/%%versionNumber%%/g, `file:${AHPath}`);
|
100
|
+
fs.writeFileSync(`${testDir}/package.json`, result);
|
101
|
+
});
|
102
|
+
|
103
|
+
test("should have made the test dir", () => {
|
104
|
+
expect(fs.existsSync(testDir)).toEqual(true);
|
105
|
+
expect(fs.existsSync(testDir + "/package.json")).toEqual(true);
|
106
|
+
});
|
107
|
+
|
108
|
+
test("can call npm install in the new project", async () => {
|
109
|
+
try {
|
110
|
+
await doCommand("npm install --ignore-scripts");
|
111
|
+
} catch (error) {
|
112
|
+
// we might get warnings about package.json locks, etc. we want to ignore them
|
113
|
+
if (error.toString().indexOf("npm") < 0) {
|
114
|
+
throw error;
|
115
|
+
}
|
116
|
+
expect(error.exitCode).toEqual(0);
|
117
|
+
}
|
118
|
+
}, 120000);
|
119
|
+
|
120
|
+
test("can generate a new project", async () => {
|
121
|
+
const { stdout } = await doCommand(`${binary} generate`);
|
122
|
+
expect(stdout).toMatch("❤️ the Actionhero Team");
|
123
|
+
|
124
|
+
[
|
125
|
+
"tsconfig.json",
|
126
|
+
"src/server.ts",
|
127
|
+
"src/actions",
|
128
|
+
"src/tasks",
|
129
|
+
"src/initializers",
|
130
|
+
"src/servers",
|
131
|
+
"src/bin",
|
132
|
+
"src/actions/swagger.ts",
|
133
|
+
"src/actions/status.ts",
|
134
|
+
"src/config",
|
135
|
+
"src/config/api.ts",
|
136
|
+
"src/config/errors.ts",
|
137
|
+
"src/config/plugins.ts",
|
138
|
+
"src/config/logger.ts",
|
139
|
+
"src/config/redis.ts",
|
140
|
+
"src/config/routes.ts",
|
141
|
+
"src/config/tasks.ts",
|
142
|
+
"src/config/web.ts",
|
143
|
+
"src/config/websocket.ts",
|
144
|
+
"pids",
|
145
|
+
"log",
|
146
|
+
"public",
|
147
|
+
"public/index.html",
|
148
|
+
"public/chat.html",
|
149
|
+
"public/swagger.html",
|
150
|
+
"public/css/cosmo.css",
|
151
|
+
"public/javascript",
|
152
|
+
"public/logo/actionhero.png",
|
153
|
+
"__tests__",
|
154
|
+
"__tests__/actions/status.ts",
|
155
|
+
".gitignore",
|
156
|
+
].forEach((f) => {
|
157
|
+
expect(fs.existsSync(testDir + "/" + f)).toEqual(true);
|
158
|
+
});
|
159
|
+
}, 20000);
|
160
|
+
|
161
|
+
test("the project can be compiled", async () => {
|
162
|
+
const { stdout } = await doCommand(`npm run build`);
|
163
|
+
expect(stdout).toMatch("tsc");
|
164
|
+
}, 20000);
|
165
|
+
|
166
|
+
test("can call the help command", async () => {
|
167
|
+
const { stdout } = await doCommand(`${binary} help`);
|
168
|
+
expect(stdout).toMatch(/generate-action/);
|
169
|
+
expect(stdout).toMatch(/Usage: actionhero \[options\] \[command\]/);
|
170
|
+
expect(stdout).toMatch(/generate-server/);
|
171
|
+
expect(stdout).toMatch(/generate-server \[options\]/);
|
172
|
+
}, 20000);
|
173
|
+
|
174
|
+
test("can call the version command (after generate)", async () => {
|
175
|
+
const { stdout } = await doCommand(`${binary} --version`);
|
176
|
+
expect(stdout).toContain("0.1.0"); // this project's version
|
177
|
+
}, 20000);
|
178
|
+
|
179
|
+
test("will show a warning with bogus input", async () => {
|
180
|
+
try {
|
181
|
+
await doCommand(`${binary} not-a-thing`);
|
182
|
+
throw new Error("should not get here");
|
183
|
+
} catch (error) {
|
184
|
+
expect(error).toBeTruthy();
|
185
|
+
expect(error.exitCode).toEqual(1);
|
186
|
+
expect(error.stderr).toMatch(/unknown command 'not-a-thing'/);
|
187
|
+
}
|
188
|
+
}, 20000);
|
189
|
+
|
190
|
+
describe("generating files", () => {
|
191
|
+
afterAll(() => {
|
192
|
+
const files = [
|
193
|
+
`${testDir}/src/actions/myAction.ts`,
|
194
|
+
`${testDir}/__tests__/actions/myAction.ts`,
|
195
|
+
`${testDir}/src/tasks/myTask.ts`,
|
196
|
+
`${testDir}/__tests__/tasks/myTask.ts`,
|
197
|
+
`${testDir}/src/bin/myCommand.ts`,
|
198
|
+
`${testDir}/src/servers/myServer.ts`,
|
199
|
+
`${testDir}/src/initializers/myInitializer.ts`,
|
200
|
+
];
|
201
|
+
|
202
|
+
files.forEach((f) => {
|
203
|
+
if (fs.existsSync(f)) {
|
204
|
+
fs.unlinkSync(f);
|
205
|
+
}
|
206
|
+
});
|
207
|
+
});
|
208
|
+
|
209
|
+
test("can generate an action", async () => {
|
210
|
+
await doCommand(
|
211
|
+
`${binary} generate-action --name=myAction --description=my_description`,
|
212
|
+
);
|
213
|
+
const actionData = String(
|
214
|
+
fs.readFileSync(`${testDir}/src/actions/myAction.ts`),
|
215
|
+
);
|
216
|
+
expect(actionData).toMatch(/export class MyAction extends Action/);
|
217
|
+
expect(actionData).toMatch(/this.name = "myAction"/);
|
218
|
+
|
219
|
+
const testData = String(
|
220
|
+
fs.readFileSync(`${testDir}/__tests__/actions/myAction.ts`),
|
221
|
+
);
|
222
|
+
expect(testData).toMatch('describe("Action: myAction"');
|
223
|
+
}, 20000);
|
224
|
+
|
225
|
+
test("can generate a task", async () => {
|
226
|
+
await doCommand(
|
227
|
+
`${binary} generate-task --name=myTask --description=my_description --queue=my_queue --frequency=12345`,
|
228
|
+
);
|
229
|
+
const taskData = String(
|
230
|
+
fs.readFileSync(`${testDir}/src/tasks/myTask.ts`),
|
231
|
+
);
|
232
|
+
expect(taskData).toMatch(/export class MyTask extends Task/);
|
233
|
+
expect(taskData).toMatch(/this.name = "myTask"/);
|
234
|
+
expect(taskData).toMatch(/this.queue = "my_queue"/);
|
235
|
+
expect(taskData).toMatch(/this.frequency = 12345/);
|
236
|
+
|
237
|
+
const testData = String(
|
238
|
+
fs.readFileSync(`${testDir}/__tests__/tasks/myTask.ts`),
|
239
|
+
);
|
240
|
+
expect(testData).toMatch('describe("Task: myTask"');
|
241
|
+
}, 20000);
|
242
|
+
|
243
|
+
test("can generate a CLI command", async () => {
|
244
|
+
await doCommand(
|
245
|
+
`${binary} generate-cli --name=myCommand --description=my_description --example=my_example`,
|
246
|
+
);
|
247
|
+
const data = String(fs.readFileSync(`${testDir}/src/bin/myCommand.ts`));
|
248
|
+
expect(data).toMatch(/this.name = "myCommand"/);
|
249
|
+
expect(data).toMatch(/this.example = "my_example"/);
|
250
|
+
}, 20000);
|
251
|
+
|
252
|
+
test("can generate a server", async () => {
|
253
|
+
await doCommand(`${binary} generate-server --name=myServer`);
|
254
|
+
const data = String(
|
255
|
+
fs.readFileSync(`${testDir}/src/servers/myServer.ts`),
|
256
|
+
);
|
257
|
+
expect(data).toMatch(/this.type = "myServer"/);
|
258
|
+
expect(data).toMatch(/canChat: false/);
|
259
|
+
expect(data).toMatch(/logConnections: true/);
|
260
|
+
expect(data).toMatch(/logExits: true/);
|
261
|
+
expect(data).toMatch(/sendWelcomeMessage: false/);
|
262
|
+
}, 20000);
|
263
|
+
|
264
|
+
test("can generate an initializer", async () => {
|
265
|
+
await doCommand(
|
266
|
+
`${binary} generate-initializer --name=myInitializer --stopPriority=123`,
|
267
|
+
);
|
268
|
+
const data = String(
|
269
|
+
fs.readFileSync(`${testDir}/src/initializers/myInitializer.ts`),
|
270
|
+
);
|
271
|
+
expect(data).toMatch(/this.loadPriority = 1000/);
|
272
|
+
expect(data).toMatch(/this.startPriority = 1000/);
|
273
|
+
expect(data).toMatch(/this.stopPriority = 123/);
|
274
|
+
expect(data).toMatch(/async initialize\(\) {/);
|
275
|
+
expect(data).toMatch(/async start\(\) {/);
|
276
|
+
expect(data).toMatch(/async stop\(\) {/);
|
277
|
+
}, 20000);
|
278
|
+
});
|
279
|
+
|
280
|
+
test("can call npm test in the new project and not fail", async () => {
|
281
|
+
// since prettier no longer works with node < 10, we need to skip this test
|
282
|
+
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)![1]);
|
283
|
+
if (nodeVersion < 10) {
|
284
|
+
console.log(
|
285
|
+
`skipping 'npm test' because this node version ${nodeVersion} < 10.0.0`,
|
286
|
+
);
|
287
|
+
return;
|
288
|
+
}
|
289
|
+
|
290
|
+
// jest writes to stderr for some reason, so we need to test for the exit code here
|
291
|
+
try {
|
292
|
+
await doCommand("npm test", true, { NODE_ENV: "test" });
|
293
|
+
} catch (error) {
|
294
|
+
if (error.exitCode !== 0) {
|
295
|
+
throw error;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}, 120000);
|
299
|
+
|
300
|
+
describe("can run the server", () => {
|
301
|
+
let serverPid: number;
|
302
|
+
|
303
|
+
beforeAll(async function () {
|
304
|
+
doCommand(`node dist/server.js`, true, { PORT: port });
|
305
|
+
await sleep(5000);
|
306
|
+
serverPid = pid;
|
307
|
+
}, 20000);
|
308
|
+
|
309
|
+
afterAll(async () => {
|
310
|
+
if (isRunning(serverPid)) {
|
311
|
+
await doCommand(`kill ${serverPid}`);
|
312
|
+
}
|
313
|
+
});
|
314
|
+
|
315
|
+
test("can boot the server", async () => {
|
316
|
+
const response = await axios(`http://${host}:${port}/api/status`);
|
317
|
+
expect(response.data.serverInformation.serverName).toEqual(
|
318
|
+
"my_actionhero_project",
|
319
|
+
);
|
320
|
+
});
|
321
|
+
|
322
|
+
test("can handle signals to reboot", async () => {
|
323
|
+
await doCommand(`kill -s USR2 ${serverPid}`);
|
324
|
+
await sleep(3000);
|
325
|
+
const response = await axios(`http://${host}:${port}/api/status`);
|
326
|
+
expect(response.data.serverInformation.serverName).toEqual(
|
327
|
+
"my_actionhero_project",
|
328
|
+
);
|
329
|
+
}, 5000);
|
330
|
+
|
331
|
+
test("can handle signals to stop", async () => {
|
332
|
+
await doCommand(`kill ${serverPid}`);
|
333
|
+
await sleep(1000);
|
334
|
+
try {
|
335
|
+
await axios.get(`http://${host}:${port}/api/status`);
|
336
|
+
throw new Error("should not get here");
|
337
|
+
} catch (error) {
|
338
|
+
if (error instanceof AxiosError) {
|
339
|
+
expect(error.toString()).toMatch(
|
340
|
+
/ECONNREFUSED|ECONNRESET|RequestError|AggregateError/,
|
341
|
+
);
|
342
|
+
} else throw error;
|
343
|
+
}
|
344
|
+
});
|
345
|
+
|
346
|
+
// test('will shutdown after the alloted time')
|
347
|
+
});
|
348
|
+
}
|
349
|
+
});
|
@@ -0,0 +1,132 @@
|
|
1
|
+
import {
|
2
|
+
api,
|
3
|
+
Process,
|
4
|
+
config,
|
5
|
+
utils,
|
6
|
+
specHelper,
|
7
|
+
chatRoom,
|
8
|
+
redis,
|
9
|
+
} from "./../../src";
|
10
|
+
|
11
|
+
const actionhero = new Process();
|
12
|
+
|
13
|
+
describe("Core: Action Cluster", () => {
|
14
|
+
beforeAll(async () => {
|
15
|
+
await actionhero.start();
|
16
|
+
for (var room in config!.general!.startingChatRooms as Record<
|
17
|
+
string,
|
18
|
+
Record<string, any>
|
19
|
+
>) {
|
20
|
+
try {
|
21
|
+
await chatRoom.destroy(room);
|
22
|
+
await chatRoom.add(room);
|
23
|
+
} catch (error) {
|
24
|
+
console.log(error);
|
25
|
+
if (
|
26
|
+
config.errors &&
|
27
|
+
typeof config.errors.connectionRoomExists === "function"
|
28
|
+
) {
|
29
|
+
throw error;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
});
|
34
|
+
|
35
|
+
afterAll(async () => await actionhero.stop());
|
36
|
+
|
37
|
+
describe("RPC", () => {
|
38
|
+
afterEach(() => {
|
39
|
+
delete api.rpcTestMethod;
|
40
|
+
});
|
41
|
+
|
42
|
+
test("can call remote methods on all other servers in the cluster", async () => {
|
43
|
+
const data: Record<string, any> = {};
|
44
|
+
api.rpcTestMethod = (arg1: any, arg2: any) => {
|
45
|
+
data[1] = [arg1, arg2];
|
46
|
+
};
|
47
|
+
await redis.doCluster("api.rpcTestMethod", ["arg1", "arg2"]);
|
48
|
+
await utils.sleep(100);
|
49
|
+
|
50
|
+
expect(data[1][0]).toEqual("arg1");
|
51
|
+
expect(data[1][1]).toEqual("arg2");
|
52
|
+
});
|
53
|
+
|
54
|
+
test("can call remote methods only on one other cluster who holds a specific connectionId", async () => {
|
55
|
+
const client = await specHelper.buildConnection();
|
56
|
+
const data: Record<string, any> = {};
|
57
|
+
api.rpcTestMethod = (arg1: any, arg2: any) => {
|
58
|
+
data[1] = [arg1, arg2];
|
59
|
+
};
|
60
|
+
|
61
|
+
await redis.doCluster("api.rpcTestMethod", ["arg1", "arg2"], client.id);
|
62
|
+
await utils.sleep(100);
|
63
|
+
|
64
|
+
expect(data[1][0]).toEqual("arg1");
|
65
|
+
expect(data[1][1]).toEqual("arg2");
|
66
|
+
client.destroy();
|
67
|
+
});
|
68
|
+
|
69
|
+
test("can get information about connections connected to other servers", async () => {
|
70
|
+
const client = await specHelper.buildConnection();
|
71
|
+
|
72
|
+
const { id, type, canChat } = await api.connections.apply(client.id);
|
73
|
+
expect(id).toEqual(client.id);
|
74
|
+
expect(type).toEqual("testServer");
|
75
|
+
expect(canChat).toEqual(true);
|
76
|
+
});
|
77
|
+
|
78
|
+
test("can call remote methods on/about connections connected to other servers", async () => {
|
79
|
+
const client = await specHelper.buildConnection();
|
80
|
+
//@ts-ignore
|
81
|
+
expect(client["auth"]).toBeUndefined();
|
82
|
+
|
83
|
+
const connection = await api.connections.apply(client.id, "set", [
|
84
|
+
"auth",
|
85
|
+
true,
|
86
|
+
]);
|
87
|
+
expect(connection.id).toEqual(client.id);
|
88
|
+
//@ts-ignore
|
89
|
+
expect(client["auth"]).toEqual(true);
|
90
|
+
client.destroy();
|
91
|
+
});
|
92
|
+
|
93
|
+
test("can send arbitrary messages to connections connected to other servers", async () => {
|
94
|
+
const client = await specHelper.buildConnection();
|
95
|
+
|
96
|
+
const connection = await api.connections.apply(client.id, "sendMessage", {
|
97
|
+
message: "hi",
|
98
|
+
});
|
99
|
+
if (!connection.messages) throw new Error("no connection.messages");
|
100
|
+
const message = connection.messages[connection.messages.length - 1];
|
101
|
+
expect(message.message).toEqual("hi");
|
102
|
+
});
|
103
|
+
|
104
|
+
test("can call api.chatRoom.removeMember on other servers", async () => {
|
105
|
+
const client = await specHelper.buildConnection();
|
106
|
+
const spy = jest
|
107
|
+
.spyOn(api.chatRoom, "removeMember")
|
108
|
+
.mockImplementationOnce(jest.fn());
|
109
|
+
await redis.doCluster("api.chatRoom.removeMember", [
|
110
|
+
client.id,
|
111
|
+
"defaultRoom",
|
112
|
+
]);
|
113
|
+
await utils.sleep(100);
|
114
|
+
|
115
|
+
expect(spy).toHaveBeenCalledWith(client.id, "defaultRoom");
|
116
|
+
});
|
117
|
+
|
118
|
+
test("failing RPC calls with a callback will have a failure callback", async () => {
|
119
|
+
try {
|
120
|
+
await redis.doCluster(
|
121
|
+
"api.rpcTestMethod",
|
122
|
+
[],
|
123
|
+
"A missing clientId",
|
124
|
+
true,
|
125
|
+
);
|
126
|
+
throw new Error("should not get here");
|
127
|
+
} catch (error) {
|
128
|
+
expect(error.toString()).toEqual("Error: RPC Timeout");
|
129
|
+
}
|
130
|
+
});
|
131
|
+
});
|
132
|
+
});
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import { promisify } from "util";
|
2
|
+
import * as fs from "fs";
|
3
|
+
import * as path from "path";
|
4
|
+
import { Process, ActionheroConfigInterface } from "./../../src";
|
5
|
+
import { buildConfig } from "./../../src/modules/config";
|
6
|
+
|
7
|
+
const actionhero = new Process();
|
8
|
+
let config: Partial<ActionheroConfigInterface>;
|
9
|
+
let configFolders: string | undefined;
|
10
|
+
|
11
|
+
const newConfigFolderPaths = [
|
12
|
+
path.join(__dirname, "first_config"),
|
13
|
+
path.join(__dirname, "second_config"),
|
14
|
+
];
|
15
|
+
|
16
|
+
const routeFilesContent = [
|
17
|
+
"export const DEFAULT = { collection: () => { return { a: 1 } } }",
|
18
|
+
"export const DEFAULT = { collection: () => { return { b: 2 } } }",
|
19
|
+
];
|
20
|
+
|
21
|
+
const createRouteFile = async (
|
22
|
+
newConfigFolderPath: string,
|
23
|
+
routeFileContent: string,
|
24
|
+
) => {
|
25
|
+
try {
|
26
|
+
await promisify(fs.mkdir)(newConfigFolderPath);
|
27
|
+
} catch (ex) {}
|
28
|
+
|
29
|
+
try {
|
30
|
+
const newRoutesFilePath = path.join(newConfigFolderPath, "collection.ts");
|
31
|
+
|
32
|
+
await promisify(fs.writeFile)(newRoutesFilePath, routeFileContent, {
|
33
|
+
encoding: "utf-8",
|
34
|
+
});
|
35
|
+
} catch (ex) {}
|
36
|
+
};
|
37
|
+
|
38
|
+
const removeRouteFile = async (newConfigFolderPath: string) => {
|
39
|
+
try {
|
40
|
+
const newRoutesFilePath = path.join(newConfigFolderPath, "collection.ts");
|
41
|
+
|
42
|
+
await promisify(fs.unlink)(newRoutesFilePath);
|
43
|
+
} catch (ex) {}
|
44
|
+
|
45
|
+
try {
|
46
|
+
await promisify(fs.rmdir)(newConfigFolderPath);
|
47
|
+
} catch (ex) {}
|
48
|
+
};
|
49
|
+
|
50
|
+
describe("Core: config folders", () => {
|
51
|
+
beforeAll(async () => {
|
52
|
+
configFolders = process.env.ACTIONHERO_CONFIG;
|
53
|
+
|
54
|
+
await removeRouteFile(newConfigFolderPaths[0]);
|
55
|
+
await removeRouteFile(newConfigFolderPaths[1]);
|
56
|
+
await createRouteFile(newConfigFolderPaths[0], routeFilesContent[0]);
|
57
|
+
await createRouteFile(newConfigFolderPaths[1], routeFilesContent[1]);
|
58
|
+
|
59
|
+
process.env.ACTIONHERO_CONFIG = newConfigFolderPaths.join(",");
|
60
|
+
|
61
|
+
await actionhero.start();
|
62
|
+
config = buildConfig();
|
63
|
+
});
|
64
|
+
|
65
|
+
afterAll(async () => {
|
66
|
+
await actionhero.stop();
|
67
|
+
await removeRouteFile(newConfigFolderPaths[0]);
|
68
|
+
await removeRouteFile(newConfigFolderPaths[1]);
|
69
|
+
process.env.ACTIONHERO_CONFIG = configFolders;
|
70
|
+
});
|
71
|
+
|
72
|
+
test("routes should be rebuilt and contain both paths", async () => {
|
73
|
+
expect(config.collection).toEqual({
|
74
|
+
a: 1,
|
75
|
+
b: 2,
|
76
|
+
});
|
77
|
+
});
|
78
|
+
});
|
@@ -0,0 +1,112 @@
|
|
1
|
+
import { api, Process, config, specHelper } from "./../../src";
|
2
|
+
|
3
|
+
const actionhero = new Process();
|
4
|
+
let originalUnknownAction: any;
|
5
|
+
let originalGenericError: any;
|
6
|
+
|
7
|
+
describe("Core", () => {
|
8
|
+
describe("errors", () => {
|
9
|
+
beforeAll(async () => {
|
10
|
+
await actionhero.start();
|
11
|
+
originalUnknownAction = config!.errors!.unknownAction;
|
12
|
+
});
|
13
|
+
|
14
|
+
afterAll(async () => {
|
15
|
+
await actionhero.stop();
|
16
|
+
config!.errors!.unknownAction = originalUnknownAction;
|
17
|
+
});
|
18
|
+
|
19
|
+
test("returns string errors properly", async () => {
|
20
|
+
const { error } = await specHelper.runAction("notARealAction");
|
21
|
+
expect(error).toEqual("Error: unknown action or invalid apiVersion");
|
22
|
+
});
|
23
|
+
|
24
|
+
test("returns Error object properly", async () => {
|
25
|
+
//@ts-ignore
|
26
|
+
config.errors.unknownAction = () => {
|
27
|
+
return new Error("error test");
|
28
|
+
};
|
29
|
+
const { error } = await specHelper.runAction("notARealAction");
|
30
|
+
expect(error).toEqual("Error: error test");
|
31
|
+
});
|
32
|
+
|
33
|
+
test("returns generic object properly", async () => {
|
34
|
+
//@ts-ignore
|
35
|
+
config.errors.unknownAction = () => {
|
36
|
+
return { code: "error111", reason: "busted" };
|
37
|
+
};
|
38
|
+
|
39
|
+
const { error } = await specHelper.runAction("notARealAction");
|
40
|
+
expect(error.code).toEqual("error111");
|
41
|
+
expect(error.reason).toEqual("busted");
|
42
|
+
});
|
43
|
+
|
44
|
+
test("can have async error handlers", async () => {
|
45
|
+
//@ts-ignore
|
46
|
+
config.errors.unknownAction = async () => {
|
47
|
+
return new Promise((resolve) => {
|
48
|
+
//@ts-ignore
|
49
|
+
setTimeout(() => resolve({ sleepy: true }), 100);
|
50
|
+
});
|
51
|
+
};
|
52
|
+
|
53
|
+
const { error } = await specHelper.runAction("notARealAction");
|
54
|
+
expect(error.sleepy).toEqual(true);
|
55
|
+
});
|
56
|
+
});
|
57
|
+
|
58
|
+
describe("Core: Errors: Custom Error Decoration", () => {
|
59
|
+
const errorMsg = "worst action ever!";
|
60
|
+
beforeAll(async () => {
|
61
|
+
await actionhero.start();
|
62
|
+
originalGenericError = config!.errors!.genericError;
|
63
|
+
api.actions.versions.errorAction = [1];
|
64
|
+
api.actions.actions.errorAction = {
|
65
|
+
// @ts-ignore
|
66
|
+
1: {
|
67
|
+
name: "errorAction",
|
68
|
+
description: "this action throws errors",
|
69
|
+
version: 1,
|
70
|
+
inputs: {},
|
71
|
+
run: async (data) => {
|
72
|
+
throw new Error(errorMsg);
|
73
|
+
},
|
74
|
+
},
|
75
|
+
};
|
76
|
+
});
|
77
|
+
|
78
|
+
afterAll(async () => {
|
79
|
+
await actionhero.stop();
|
80
|
+
delete api.actions.actions.errorAction;
|
81
|
+
delete api.actions.versions.errorAction;
|
82
|
+
config!.errors!.genericError = originalGenericError;
|
83
|
+
});
|
84
|
+
|
85
|
+
test("will return an actions error", async () => {
|
86
|
+
const response = await specHelper.runAction<any>("errorAction");
|
87
|
+
expect(response.error).toEqual("Error: worst action ever!");
|
88
|
+
expect(response.requestId).toBeUndefined();
|
89
|
+
});
|
90
|
+
|
91
|
+
test("can decorate an error", async () => {
|
92
|
+
config!.errors!.genericError = async (data, error) => {
|
93
|
+
data.response.requestId = "id-12345";
|
94
|
+
return error;
|
95
|
+
};
|
96
|
+
const response = await specHelper.runAction<any>("errorAction");
|
97
|
+
expect(response.error).toEqual("Error: worst action ever!");
|
98
|
+
expect(response.requestId).toEqual("id-12345");
|
99
|
+
});
|
100
|
+
|
101
|
+
test("exceptionHandlers get action errors", async () => {
|
102
|
+
await new Promise(async (resolve) => {
|
103
|
+
api.exceptionHandlers.reporters.push((error) => {
|
104
|
+
expect(error.message).toMatch(/worst action ever/);
|
105
|
+
resolve(null);
|
106
|
+
});
|
107
|
+
|
108
|
+
const response = await specHelper.runAction("errorAction");
|
109
|
+
});
|
110
|
+
});
|
111
|
+
});
|
112
|
+
});
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { log, loggers, Process } from "./../../src";
|
2
|
+
|
3
|
+
const actionhero = new Process();
|
4
|
+
|
5
|
+
describe("Core", () => {
|
6
|
+
describe("log", () => {
|
7
|
+
beforeAll(async () => {
|
8
|
+
await actionhero.start();
|
9
|
+
});
|
10
|
+
|
11
|
+
afterAll(async () => {
|
12
|
+
await actionhero.stop();
|
13
|
+
});
|
14
|
+
|
15
|
+
test("the log method should work", () => {
|
16
|
+
log("hello");
|
17
|
+
});
|
18
|
+
|
19
|
+
test("the winston loggers are available via the export loggers", () => {
|
20
|
+
expect(loggers.length).toBe(2);
|
21
|
+
});
|
22
|
+
});
|
23
|
+
});
|