diff --git a/.eslintignore b/.eslintignore index b7e9951..dfda330 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ -jest.config.js \ No newline at end of file +jest.config.js +dist/ \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..3867a0f --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm run lint diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 0000000..1262e27 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,2 @@ +npm run build +npm test \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b7aa05f..681b18e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "http-proxy-middleware": "^2.0.6", + "husky": "^9.0.11", "jest": "^29.7.0", "nodemon": "^3.0.3", "prettier": "3.2.5", @@ -3654,6 +3655,21 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "dev": true, + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", diff --git a/package.json b/package.json index 2c4a8f5..6a9c993 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,11 @@ "main": "index.ts", "scripts": { "dev": "nodemon ./src/index.ts", + "build": "tsc", + "start": "node ./dist/index.js", "test": "jest", - "lint": "eslint ." + "lint": "eslint .", + "prepare": "husky" }, "author": "", "license": "ISC", @@ -25,6 +28,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "http-proxy-middleware": "^2.0.6", + "husky": "^9.0.11", "jest": "^29.7.0", "nodemon": "^3.0.3", "prettier": "3.2.5", diff --git a/src/router.ts b/src/router.ts index e6a0d91..928931c 100644 --- a/src/router.ts +++ b/src/router.ts @@ -2,7 +2,6 @@ import express from "express"; import { parseConfig } from "./utils/config/parse-config"; import { createProxies } from "./utils/proxy/create-proxies"; -// TODO: prevent the app from crashing if the config is invalid const config = parseConfig("config.yml"); const router = express.Router(); diff --git a/src/utils/config/validate-config.test.ts b/src/utils/config/validate-config.test.ts new file mode 100644 index 0000000..748253f --- /dev/null +++ b/src/utils/config/validate-config.test.ts @@ -0,0 +1,96 @@ +import { validateConfig } from "./validate-config"; +import { Config } from "../../types/config"; + +describe("validateConfig", () => { + it("should not throw an error if config is valid", () => { + const validConfig: Config = { + endpoints: [ + { + name: "endpoint1", + method: "GET", + path: "/path1", + destination: { + url: "http://example.com", + }, + }, + { + name: "endpoint2", + method: "POST", + path: "/path2", + destination: { + url: "http://example.com", + }, + }, + ], + }; + + expect(() => validateConfig(validConfig)).not.toThrow(); + }); + + it("should throw an error if endpoint names are duplicated", () => { + const invalidConfig: Config = { + endpoints: [ + { + name: "duplicate", + method: "GET", + path: "/path1", + destination: { + url: "http://example.com", + }, + }, + { + name: "duplicate", + method: "GET", + path: "/path2", + destination: { + url: "http://example.com", + }, + }, + ], + }; + + expect(() => validateConfig(invalidConfig)).toThrow(); + }); + + it("should throw an error if endpoint path/method combination is duplicated", () => { + const invalidConfig: Config = { + endpoints: [ + { + name: "endpoint1", + method: "GET", + path: "/duplicate", + destination: { + url: "http://example.com", + }, + }, + { + name: "endpoint2", + method: "GET", + path: "/duplicate", + destination: { + url: "http://example.com", + }, + }, + ], + }; + + expect(() => validateConfig(invalidConfig)).toThrow(); + }); + + it("should throw an error if destination URL is invalid", () => { + const invalidUrlConfig: Config = { + endpoints: [ + { + name: "endpoint", + method: "GET", + path: "/path", + destination: { + url: "invalid_url", + }, + }, + ], + }; + + expect(() => validateConfig(invalidUrlConfig)).toThrow(); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..69f3c12 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "outDir": "./dist" + } +}