aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin J Hoerr <kjhoerr@protonmail.com>2020-04-27 18:17:06 -0400
committerKevin J Hoerr <kjhoerr@protonmail.com>2020-04-27 18:17:06 -0400
commitfd2013a7bf4129b05080f01ef199d69b9ef9ec68 (patch)
tree8f8eae08b2884a0e066e01138aa6a104625d952f
parent32db5f6e5e8b6f6d2baffbec44cc34daedd24533 (diff)
downloadao-coverage-fd2013a7bf4129b05080f01ef199d69b9ef9ec68.tar.gz
ao-coverage-fd2013a7bf4129b05080f01ef199d69b9ef9ec68.tar.bz2
ao-coverage-fd2013a7bf4129b05080f01ef199d69b9ef9ec68.zip
Refactor startup to passthrough values
Even more refactoring - however there were some small troubles using path in the nested scripts/files, so referencing them via the index should be a bit more stable. Plus, the config unit tests won't just exit because of configOrError constants strewn about the file.
-rw-r--r--src/index.ts11
-rw-r--r--src/routes.test.ts2
-rw-r--r--src/routes.ts13
-rw-r--r--src/util/config.test.ts38
-rw-r--r--src/util/config.ts53
5 files changed, 67 insertions, 50 deletions
diff --git a/src/index.ts b/src/index.ts
index e4f8db1..caa4235 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,22 +2,27 @@ import dotenv from "dotenv";
import express from "express";
import winston from "winston";
import expressWinston from "express-winston";
+import path from "path";
dotenv.config();
import routes from "./routes";
import Metadata from "./metadata";
import loggerConfig from "./util/logger";
-import { handleStartup, handleShutdown } from "./util/config";
+import { configOrError, handleStartup, handleShutdown } from "./util/config";
// Start-up configuration
const BIND_ADDRESS = process.env.BIND_ADDRESS ?? "localhost";
const MONGO_DB = process.env.MONGO_DB ?? "ao-coverage";
const PORT = Number(process.env.PORT ?? 3000);
+const MONGO_URI = configOrError("MONGO_URI");
+const TARGET_URL = process.env.TARGET_URL ?? "http://localhost:3000";
+const PUBLIC_PATH = path.join(__dirname, "..", "public");
+const HOST_DIR = configOrError("HOST_DIR");
const logger = winston.createLogger(loggerConfig("ROOT"));
-handleStartup().then(mongo => {
+handleStartup(MONGO_URI, HOST_DIR, PUBLIC_PATH, TARGET_URL).then(mongo => {
const app: express.Application = express();
const metadata = new Metadata(mongo.db(MONGO_DB));
@@ -32,7 +37,7 @@ handleStartup().then(mongo => {
);
// actual app routes
- app.use(routes(metadata));
+ app.use(routes(metadata, PUBLIC_PATH));
app.use(expressWinston.errorLogger(loggerConfig("_ERR")));
diff --git a/src/routes.test.ts b/src/routes.test.ts
index 90084f6..3a6bd26 100644
--- a/src/routes.test.ts
+++ b/src/routes.test.ts
@@ -38,7 +38,7 @@ const mock = (
const request = (mockMeta: MetadataMockType = mock()): SuperTest<Test> => {
const app = express();
- app.use(routes(mockMeta as Metadata));
+ app.use(routes(mockMeta as Metadata, path.join(__dirname, "..", "public")));
return _request(app);
};
diff --git a/src/routes.ts b/src/routes.ts
index 7ebb610..98260fa 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -17,7 +17,7 @@ const HOST_DIR = configOrError("HOST_DIR");
const logger = winston.createLogger(loggerConfig("HTTP"));
-export default (metadata: Metadata): Router => {
+export default (metadata: Metadata, publicPath: string): Router => {
const router = Router();
// serve landing page
@@ -33,15 +33,12 @@ export default (metadata: Metadata): Router => {
})
);
- // serve static files
// favicon should be served directly on root
router.get("/favicon.ico", (_, res) => {
- res.sendFile(path.join(__dirname, "..", "public", "static", "favicon.ico"));
+ res.sendFile(path.join(publicPath, "static", "favicon.ico"));
});
- router.use(
- "/static",
- express.static(path.join(__dirname, "..", "public", "static"))
- );
+ // serve static files
+ router.use("/static", express.static(path.join(publicPath, "static")));
// Upload HTML file
router.post("/v1/:org/:repo/:branch/:commit.html", (req, res) => {
@@ -219,7 +216,7 @@ export default (metadata: Metadata): Router => {
router.use((_, res) => {
res.status(404);
- res.sendFile(path.join(__dirname, "..", "public", "static", "404.html"));
+ res.sendFile(path.join(publicPath, "static", "404.html"));
});
return router;
diff --git a/src/util/config.test.ts b/src/util/config.test.ts
index b1e7df3..a065c47 100644
--- a/src/util/config.test.ts
+++ b/src/util/config.test.ts
@@ -179,6 +179,9 @@ describe("handleStartup", () => {
exit.mockClear();
});
+ const confStartup = (): Promise<MongoClient> =>
+ handleStartup("", "/apple", "/public", "localhost");
+
it("should pass back MongoClient", async () => {
const superClient = {} as MongoClient;
const fsAccess = jest.spyOn(fs.promises, "access").mockResolvedValue();
@@ -193,7 +196,7 @@ describe("handleStartup", () => {
(template: templates.Template) => new Promise(res => res(template))
);
- const result = await handleStartup();
+ const result = await confStartup();
expect(fsAccess).toHaveBeenCalledTimes(1);
expect(pathAbsolute).toHaveBeenCalledTimes(1);
@@ -209,16 +212,29 @@ describe("handleStartup", () => {
});
it("should exit if HOST_DIR is not read/write accessible", async () => {
+ const superClient = {} as MongoClient;
const fsAccess = jest.spyOn(fs.promises, "access").mockRejectedValue("boo");
const pathAbsolute = jest.spyOn(path, "isAbsolute").mockReturnValue(true);
const pathJoin = jest.spyOn(path, "join").mockReturnValue("path");
+ const mongoClient = jest.spyOn(MongoClient, "connect").mockImplementation(
+ () => new Promise<MongoClient>(res => res(superClient))
+ );
+ const processTemplate = jest
+ .spyOn(templates, "default")
+ .mockImplementation(
+ (template: templates.Template) => new Promise(res => res(template))
+ );
- const result = await handleStartup();
+ const result = await confStartup();
expect(fsAccess).toHaveBeenCalledTimes(1);
expect(exit).toHaveBeenCalledWith(1);
expect(pathAbsolute).not.toHaveBeenCalled();
+ expect(mongoClient).not.toHaveBeenCalled();
+ expect(processTemplate).not.toHaveBeenCalled();
expect(result).toBeUndefined();
+ processTemplate.mockRestore();
+ mongoClient.mockRestore();
pathAbsolute.mockRestore();
pathJoin.mockRestore();
fsAccess.mockRestore();
@@ -232,11 +248,21 @@ describe("handleStartup", () => {
const mongoClient = jest.spyOn(MongoClient, "connect").mockImplementation(
() => new Promise<MongoClient>(res => res(superClient))
);
+ const processTemplate = jest
+ .spyOn(templates, "default")
+ .mockImplementation(
+ (template: templates.Template) => new Promise(res => res(template))
+ );
- await handleStartup();
+ const result = await confStartup();
+ expect(fsAccess).toHaveBeenCalledTimes(1);
expect(pathAbsolute).toHaveBeenCalledTimes(1);
expect(exit).toHaveBeenCalledWith(1);
+ expect(mongoClient).not.toHaveBeenCalled();
+ expect(processTemplate).not.toHaveBeenCalled();
+ expect(result).toBeUndefined();
+ processTemplate.mockRestore();
mongoClient.mockRestore();
pathAbsolute.mockRestore();
pathJoin.mockRestore();
@@ -258,10 +284,14 @@ describe("handleStartup", () => {
(template: templates.Template) => new Promise(res => res(template))
);
- await handleStartup();
+ const result = await confStartup();
+ expect(fsAccess).toHaveBeenCalledTimes(1);
+ expect(pathAbsolute).toHaveBeenCalledTimes(1);
expect(mongoClient).toHaveBeenCalledTimes(1);
expect(exit).toHaveBeenCalledWith(1);
+ expect(processTemplate).not.toHaveBeenCalled();
+ expect(result).toBeUndefined();
processTemplate.mockRestore();
mongoClient.mockRestore();
pathAbsolute.mockRestore();
diff --git a/src/util/config.ts b/src/util/config.ts
index 195d85b..3eb8d1c 100644
--- a/src/util/config.ts
+++ b/src/util/config.ts
@@ -45,48 +45,33 @@ export const persistTemplate = async (input: Template): Promise<void> => {
}
};
-const MONGO_URI = configOrError("MONGO_URI");
-const TARGET_URL = process.env.TARGET_URL ?? "http://localhost:3000";
-const HOST_DIR = configOrError("HOST_DIR");
-
-export const handleStartup = async (): Promise<MongoClient> => {
+export const handleStartup = async (
+ mongoUri: string,
+ hostDir: string,
+ publicPath: string,
+ targetUrl: string
+): Promise<MongoClient> => {
try {
- await fs.promises.access(HOST_DIR, fs.constants.R_OK | fs.constants.W_OK);
- if (!path.isAbsolute(HOST_DIR)) {
- logger.error("HOST_DIR must be an absolute path");
- process.exit(1);
+ await fs.promises.access(hostDir, fs.constants.R_OK | fs.constants.W_OK);
+ if (!path.isAbsolute(hostDir)) {
+ await Promise.reject("hostDir must be an absolute path");
}
- const mongo = await MongoClient.connect(MONGO_URI, {
+ const mongo = await MongoClient.connect(mongoUri, {
useUnifiedTopology: true
- }).catch((err: MongoError) => {
- logger.error(err.message ?? "Unable to connect to database");
- process.exit(1);
- });
+ }).catch((err: MongoError) =>
+ Promise.reject(err.message ?? "Unable to connect to database")
+ );
await persistTemplate({
- inputFile: path.join(
- __dirname,
- "..",
- "..",
- "public",
- "templates",
- "bash.template"
- ),
- outputFile: path.join(HOST_DIR, "bash"),
- context: { TARGET_URL }
+ inputFile: path.join(publicPath, "templates", "bash.template"),
+ outputFile: path.join(hostDir, "bash"),
+ context: { targetUrl }
} as Template);
await persistTemplate({
- inputFile: path.join(
- __dirname,
- "..",
- "..",
- "public",
- "templates",
- "index.html.template"
- ),
- outputFile: path.join(HOST_DIR, "index.html"),
- context: { TARGET_URL }
+ inputFile: path.join(publicPath, "templates", "index.html.template"),
+ outputFile: path.join(hostDir, "index.html"),
+ context: { targetUrl }
} as Template);
return mongo;