Skip to content

Commit

Permalink
chore: add couple more tests and show coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
brunotot committed Oct 24, 2024
1 parent 1b8f0b9 commit e11086f
Show file tree
Hide file tree
Showing 25 changed files with 217 additions and 46 deletions.
27 changes: 17 additions & 10 deletions .env
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
# GitHub repository handle
#GH_REPO_HANDLE=mern-monorepo-starter
# CHANGE ME
GH_REPO_HANDLE=mern-monorepo-starter

# GitHub author handle
#GH_AUTHOR_HANDLE=brunotot
# CHANGE ME
GH_AUTHOR_HANDLE=brunotot

# GitHub author name
#GH_AUTHOR_NAME=Bruno Tot
# CHANGE ME
GH_AUTHOR_NAME=Bruno Tot

# Custom app name (handle format)
# This changes the name of file `packages/mern-sample-app`
#CUSTOM_APP_ID=mern-sample-app
# CHANGE ME
CUSTOM_APP_ID=mern-sample-app

# Custom app name (human format)
#CUSTOM_APP_NAME=MERN Sample App
# CHANGE ME
CUSTOM_APP_NAME=MERN Sample App

GH_REPO_HANDLE=mern-monorepo-starter
GH_AUTHOR_HANDLE=brunotot
GH_AUTHOR_NAME=Bruno Tot
CUSTOM_APP_ID=mern-sample-app
CUSTOM_APP_NAME=MERN Sample App
# Keycloak URL
# CHANGE ME
KEYCLOAK_URL=https://keycloak-production-86f5.up.railway.app

# Express URL
# CHANGE ME
EXPRESS_URL=https://monorepo-mern-railway-starter-backend.up.railway.app
51 changes: 51 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
{
"version": "2.0.0",
"configurations": [
{
"name": "@org: renameProjectLabels",
"runtimeArgs": ["run", "renameProjectLabels"],
"presentation": { "group": "1" },
// Defaults below
"runtimeVersion": "21.7.0",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"console": "integratedTerminal",
"outputCapture": "std",
"autoAttachChildProcesses": false
},
{
"name": "@org: test",
"runtimeArgs": ["run", "test"],
Expand Down Expand Up @@ -131,6 +144,25 @@
"outputCapture": "std",
"autoAttachChildProcesses": false
},

{
"name": "@org/app-node-express: test:coverage",
"runtimeArgs": [
"run",
"test:coverage",
"--prefix",
"packages/mern-sample-app/app-node-express"
],
"presentation": { "group": "3" },
// Defaults below
"runtimeVersion": "21.7.0",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"console": "integratedTerminal",
"outputCapture": "std",
"autoAttachChildProcesses": false
},
{
"name": "@org/app-node-express: build",
"runtimeArgs": ["run", "build", "--prefix", "packages/mern-sample-app/app-node-express"],
Expand Down Expand Up @@ -383,6 +415,25 @@
"outputCapture": "std",
"autoAttachChildProcesses": false
},

{
"name": "@org/lib-api-client: test:coverage",
"runtimeArgs": [
"run",
"test:coverage",
"--prefix",
"packages/mern-sample-app/lib-api-client"
],
"presentation": { "group": "6" },
// Defaults below
"runtimeVersion": "21.7.0",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"console": "integratedTerminal",
"outputCapture": "std",
"autoAttachChildProcesses": false
},
{
"name": "@org/lib-api-client: build",
"runtimeArgs": ["run", "build", "--prefix", "packages/mern-sample-app/lib-api-client"],
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@ TOC

2. <details><summary>Install dependencies</summary><hr>Install dependencies with <code>pnpm</code>.<pre>pnpm install</pre><hr></details>

3. <details><summary>Configure environment variables</summary><hr>Configure <code>.env.development.local</code> variables for <b>app-node-express</b> (see env schema defined at <a href="https://github.com/brunotot/mern-monorepo-starter/blob/main/packages/app-node-express/src/setup/env.setup.ts#L13">env.setup.ts</a><hr></details>
3. <details><summary>Configure environment variables</summary><hr>Configure <code>.env.development.local</code> variables for <b>app-node-express</b> (see env schema defined at <a href="https://github.com/brunotot/mern-monorepo-starter/blob/main/packages/app-node-express/src/server/env.ts#L13">@org/app-node-express/env</a>)<hr></details>

4. <details><summary>Run a sample app locally</summary><hr>You can now run your <b>app-node-express</b> with:<pre>pnpm run app-node-express:dev</pre><hr></details>
4. <details><summary>Rename project labels</summary><hr>Configure <code>.env</code> variables, located at root of monorepo (see env file defined at <a href="https://github.com/brunotot/mern-monorepo-starter/blob/main/env.ts#L13">@org/env</a>). Afterwards, run the rename script<pre>pnpm run renameProjectLabels</pre><hr></details>

5. <details><summary>Run a sample app locally</summary><hr>You can now run your <b>app-node-express</b> with:<pre>pnpm run app-node-express:dev</pre><hr></details>

## 🚢 Deploy

Expand Down
6 changes: 6 additions & 0 deletions packages/mern-sample-app/app-node-express/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SERVER_DOMAIN=http://localhost
SERVER_PORT=8081
SERVER_ENV=development
DATABASE_NAME=production
KEYCLOAK_URL=http://localhost:8080
CORS_ALLOWED_ORIGINS=http://localhost:8081,http://localhost:5173
5 changes: 5 additions & 0 deletions packages/mern-sample-app/app-node-express/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SERVER_DOMAIN=https://monorepo-mern-railway-starter-backend.up.railway.app
SERVER_ENV=production
DATABASE_NAME=production
KEYCLOAK_URL=https://keycloak-production-86f5.up.railway.app
CORS_ALLOWED_ORIGINS=https://monorepo-mern-railway-starter-frontend.up.railway.app,http://localhost:5173
2 changes: 0 additions & 2 deletions packages/mern-sample-app/app-node-express/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

.env
.env.*
.env.*.local

node_modules
Expand Down
2 changes: 1 addition & 1 deletion packages/mern-sample-app/app-node-express/nodemon.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"watch": ["src", "../lib-commons/src", "../lib-api-client/src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts", "src/**/*.test.ts"],
"exec": "bash start.sh"
"exec": "export SERVER_ENV=development && bash start.sh"
}
3 changes: 2 additions & 1 deletion packages/mern-sample-app/app-node-express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"type": "module",
"scripts": {
"test": "npm run build --silent && export SERVER_VERSION=$(grep -o '\"version\": *\"[^\"]*\"' package.json | awk -F'\"' '{print $4}') && vitest --run",
"test:coverage": "npm run build --silent && export SERVER_VERSION=$(grep -o '\"version\": *\"[^\"]*\"' package.json | awk -F'\"' '{print $4}') && vitest --run --coverage",
"build": "rm -rf dist && tsc --build && tsc-alias -p tsconfig.prod.json",
"start": "bash start.sh",
"start": "export SERVER_ENV=production && bash start.sh",
"dev": "nodemon",
"clean": "rm -rf ./dist",
"lint": "eslint src",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ export class IocRegistry {
componentClasses,
dependencySchema,
);
sortedInjectionClasses.forEach(Class => {
for (const Class of sortedInjectionClasses) {
const name = IocClassMetadata.getInstance(Class).getName();
const instance = new Class();
this.registry.set(name, instance);
});
}
}

#getSortedInjectionClasses(classes: NoArgsClass[], dependencySchema: Record<string, string[]>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class KeycloakDao {
protected async put<T>(path: string, body: Partial<T>): Promise<T> {
const endpoint = this.endpoint(path);
const config = await this.buildConfig();
const response = await fetch(endpoint, {
const response = await this.doFetch(endpoint, {
headers: {
...config.headers,
"Content-Type": "application/json",
Expand All @@ -45,10 +45,21 @@ export class KeycloakDao {
return body as T;
}

private async doFetch(endpoint: string, config: RequestInit): Promise<Response> {
let response: Response;
try {
response = await fetch(endpoint, config);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
throw new Error("Keycloak server is not available");
}
return response;
}

protected async get<T>(path: string): Promise<T> {
const endpoint = this.endpoint(path);
const config = await this.buildConfig();
const response = await fetch(endpoint, config);
const response = await this.doFetch(endpoint, config);
if (!response.ok) throw new Error(`Failed to GET data: ${response.statusText}`);
const jsonResponse = (await response.json()) as T;
return jsonResponse;
Expand All @@ -57,7 +68,7 @@ export class KeycloakDao {
protected async post<T>(path: string, body: T): Promise<T> {
const endpoint = this.endpoint(path);
const config = await this.buildConfig();
const response = await fetch(endpoint, {
const response = await this.doFetch(endpoint, {
headers: {
...config.headers,
"Content-Type": "application/json",
Expand All @@ -72,7 +83,7 @@ export class KeycloakDao {
protected async delete(path: string): Promise<void> {
const endpoint = this.endpoint(path);
const config = await this.buildConfig();
const response = await fetch(endpoint, {
const response = await this.doFetch(endpoint, {
...config,
method: "DELETE",
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ export class KeycloakTokenManager {

public async getToken(): Promise<string> {
if (this.cachedTokenValid) return this.cachedToken;
const response = await fetch(this.KEYCLOAK_LOGIN_URL, this.buildLoginConfig());
let response: Response;
try {
response = await fetch(this.KEYCLOAK_LOGIN_URL, this.buildLoginConfig());
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
throw new Error("Keycloak is not available");
}
if (!response.ok) throw new Error(`Failed to fetch token: ${response.statusText}`);
const result = (await response.json()) as Keycloak.Authentication;
const { access_token, expires_in } = result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class ExpressApp {
"🟢 NodeJS": process.version,
"🏠 Env": env.SERVER_ENV,
"🔑 Keycloak": this.keycloakUrl,
"📝 Swagger": env.SWAGGER_ENDPOINT,
"📝 Swagger": "/api-docs",
"🆔 PID": `${process.pid}`,
"🧠 Memory": this.memoryUsage,
"📅 Started": new Date().toLocaleString(),
Expand Down Expand Up @@ -168,11 +168,11 @@ export class ExpressApp {
#initializeSwagger() {
initializeSwagger({
app: this.expressApp,
oauth2RedirectUrl: `${this.#url}${env.SWAGGER_ENDPOINT}${env.SWAGGER_OAUTH2_REDIRECT_ENDPOINT}`,
oauth2RedirectUrl: `${this.#url}/api-docs${env.SWAGGER_OAUTH2_REDIRECT_ENDPOINT}`,
oauth2AuthorizationUrl: `${env.KEYCLOAK_URL}/realms/${env.KEYCLOAK_REALM}/protocol/openid-connect${env.KEYCLOAK_LOGIN_ENDPOINT}`,
oauth2TokenUrl: `${env.KEYCLOAK_URL}/realms/${env.KEYCLOAK_REALM}/protocol/openid-connect${env.KEYCLOAK_TOKEN_ENDPOINT}`,
version: env.SERVER_VERSION,
endpoint: env.SWAGGER_ENDPOINT,
endpoint: "/api-docs",
cssPath: env.SWAGGER_CSS_PATH,
jsPath: env.SWAGGER_JS_PATH,
});
Expand Down
13 changes: 4 additions & 9 deletions packages/mern-sample-app/app-node-express/src/server/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@
* | CORS_ALLOWED_ORIGINS | List of comma-separated allowed origin patterns | `*` | `http://localhost:5173` | ⚫ |
* | CORS_ALLOWED_METHODS | List of comma-separated allowed request methods | `GET,POST,PUT,DELETE,PATCH` | `GET,POST` | ⚫ |
* | CORS_ALLOWED_HEADERS | List of comma-separated allowed request headers | `*` | `X-Custom-Header` | ⚫ |
* | SWAGGER_ENDPOINT | Swagger endpoint | `/api-docs` | `/my-swagger-endpoint` | ⚫ |
* | SWAGGER_CSS_PATH | Swagger CSS path | `/css/swagger.css` | `http://localhost:5173` | ⚫ |
* | SWAGGER_JS_PATH | Swagger JS path | `/js/swagger.js` | `GET,POST` | ⚫ |
* | SWAGGER_OAUTH2_REDIRECT_ENDPOINT | Swagger OAuth2 redirect URL | `/oauth2-redirect.html` | `/oauth2-redirect.html` | ⚫ |
Expand Down Expand Up @@ -199,12 +198,6 @@ const ENVIRONMENT_VARS = z.object({
*/
KEYCLOAK_BEARER_ONLY: z.string().default("true").transform(Transform.BOOLEAN),

/**
* The endpoint URL for Swagger documentation.
* @default "/api-docs"
*/
SWAGGER_ENDPOINT: z.string().default("/api-docs").transform(Transform.URL),

/**
* The path to the Swagger CSS file.
* @default "/css/swagger.css"
Expand Down Expand Up @@ -304,8 +297,10 @@ function filterEnvBySchema() {
}

function configLocalDotenv() {
// Make sure this function only accesses process.env and not local env (it is not initialized yet).
dotenv.config({
path: path.join(process.cwd(), `.env.${process.env.SERVER_ENV ?? "development"}.local`),
path: [
path.join(process.cwd(), `.env.${process.env.SERVER_ENV!}`),
path.join(process.cwd(), `.env.${process.env.SERVER_ENV!}.local`),
],
});
}
7 changes: 7 additions & 0 deletions packages/mern-sample-app/app-node-express/vitest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ export default defineConfig({
environment: "node",
globalSetup: "test/config/setup/globalSetup.ts",
setupFiles: ["test/config/setup/setupFiles.ts"],

coverage: {
all: true,
include: ["src/**/*.ts"],
exclude: ["src/**/index.ts", "src/main.ts"],
reporter: ["text"],
},
},
resolve: {
alias: {
Expand Down
5 changes: 0 additions & 5 deletions packages/mern-sample-app/app-vite-react/.env.development
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
# Railway development server
#VITE_API_CLIENT_URL=https://monorepo-mern-railway-starter-backend.up.railway.app
#VITE_API_KEYCLOAK_URL=https://keycloak-production-86f5.up.railway.app

# Local
VITE_API_CLIENT_URL=http://localhost:8081
VITE_API_KEYCLOAK_URL=http://localhost:8080
3 changes: 2 additions & 1 deletion packages/mern-sample-app/lib-api-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"clean": "rm -rf ./dist",
"test": "npm run build --silent && vitest --run",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
"lint:fix": "eslint src --fix",
"test:coverage": "npm run build --silent && vitest --run --coverage"
},
"keywords": [],
"author": "Bruno Tot",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { z } from "@org/lib-commons";

import { routeCommonProps, zodResponse, initContract } from "../../lib/@ts-rest";
import { UserDto, UserForm } from "../models";
import { PaginationOptionsQueryParam } from "../utils/common-models";
import * as errors from "../utils/error-responses";
import * as responses from "../utils/valid-responses";
import { routeCommonProps, zodResponse, initContract } from "../../lib/@ts-rest";

const routeDefaults = routeCommonProps({
groupName: "Users",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { zod } from "@org/lib-commons";

import { z } from "@org/lib-commons";

import { UserRepresentation } from "../../../lib";
import { UserRepresentation } from "../../../lib/keycloak/api/UserRepresentation";
import { Role } from "../domain/Role";

export const UserForm = UserRepresentation.extend({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ export function routeCommonProps<TGroupName extends string, TContextPath extends

export function zodResponse<const T extends zod.ZodTypeAny>(
zodSchema: T,
customDescription: string = "",
customDescription: string,
): typeof zodSchema {
if (!customDescription) return zodSchema;
const metaOpenApi = (zodSchema as TODO).metaOpenApi ?? {};
const title: string | undefined = metaOpenApi.title;
const description: string | undefined = metaOpenApi.description;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="@types/jest" />

import { z } from "@org/lib-commons/src/lib/zod/zod";
import { z } from "@org/lib-commons";

import { JsonQueryParam } from "../src/app/utils/query-params/JsonQueryParam";

describe("JsonQueryParam", () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/mern-sample-app/lib-api-client/test/TsRestOpenApi.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/// <reference types="@types/jest" />

import { buildOpenAPIObject } from "../src/lib/@ts-rest/TsRestOpenApi";

describe("contracts", () => {
describe("given the contracts have valid values filled", () => {
it("should not throw error", async () => {
buildOpenAPIObject({
authorizationUrl: "authorizationUrl",
tokenUrl: "tokenUrl",
version: "version",
});
});
});
});
Loading

0 comments on commit e11086f

Please sign in to comment.