Improve test coverage

This commit is contained in:
Silas 2024-09-12 09:07:19 -04:00
parent 6bdf7a32a9
commit b2a0cafe6e
8 changed files with 231 additions and 6 deletions

View File

@ -17,6 +17,7 @@
"rules": {
"@typescript-eslint/no-inferrable-types": 0,
"@typescript-eslint/no-unused-vars": 2,
"@typescript-eslint/no-var-requires": 0
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-explicit-any": "off"
}
}

View File

@ -0,0 +1,33 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
describe("Environment Configuration", () => {
const originalEnv = process.env;
beforeEach(() => {
vi.resetModules();
process.env = { ...originalEnv };
});
afterEach(() => {
process.env = originalEnv;
});
it("should set NODE_ENV to development by default", async () => {
delete process.env.NODE_ENV;
const { envs, isProduction } = await import("./index");
expect(envs.NODE_ENV).toBe("development");
expect(isProduction).toBe(false);
});
it("should use existing NODE_ENV if set", async () => {
process.env.NODE_ENV = "production";
const { envs, isProduction } = await import("./index");
expect(envs.NODE_ENV).toBe("production");
expect(isProduction).toBe(true);
});
it("should export config from dotenv-flow", async () => {
const { config } = await import("./index");
expect(config).toBeDefined();
});
});

View File

@ -0,0 +1,68 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { $log } from "@tsed/common";
vi.mock("@tsed/common", () => ({
$log: {
appenders: {
set: vi.fn()
}
}
}));
describe("Logger Configuration", () => {
const originalEnv = process.env;
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
process.env = { ...originalEnv };
});
afterEach(() => {
process.env = originalEnv;
vi.restoreAllMocks();
});
it("should not set appenders in non-production environment", async () => {
process.env.NODE_ENV = "development";
await import("./index");
expect($log.appenders.set).not.toHaveBeenCalled();
});
it("should set appenders in production environment", async () => {
process.env.NODE_ENV = "production";
await import("./index");
expect($log.appenders.set).toHaveBeenCalledTimes(2);
expect($log.appenders.set).toHaveBeenCalledWith("stdout", {
type: "stdout",
levels: ["info", "debug"],
layout: {
type: "json"
}
});
expect($log.appenders.set).toHaveBeenCalledWith("stderr", {
levels: ["trace", "fatal", "error", "warn"],
type: "stderr",
layout: {
type: "json"
}
});
});
it("should export correct DILoggerOptions in non-production", async () => {
process.env.NODE_ENV = "development";
const { default: loggerOptions } = await import("./index");
expect(loggerOptions).toEqual({
disableRoutesSummary: false
});
});
it("should export correct DILoggerOptions in production", async () => {
process.env.NODE_ENV = "production";
const { default: loggerOptions } = await import("./index");
expect(loggerOptions).toEqual({
disableRoutesSummary: true
});
});
});

View File

@ -0,0 +1,38 @@
import { IndexController } from "./IndexController";
import { describe, it, expect, beforeEach } from "vitest";
describe("IndexController", () => {
let controller: IndexController;
beforeEach(() => {
controller = new IndexController();
// @ts-expect-error - swagger is private
controller.swagger = [
{ path: "/api-docs", specVersion: "3.0.1" },
{ path: "/api-docs-2", specVersion: "3.0.2" }
];
});
it("should return correct view data", () => {
const protocol = "https";
const host = "example.com";
const result = controller.get(protocol, host);
expect(result).toEqual({
BASE_URL: "https://example.com",
docs: [
{ url: "https://example.com/api-docs", path: "/api-docs", specVersion: "3.0.1" },
{ url: "https://example.com/api-docs-2", path: "/api-docs-2", specVersion: "3.0.2" }
]
});
});
it("should use http when protocol is not provided", () => {
const host = "example.com";
const result = controller.get("", host);
expect(result.BASE_URL).toBe("http://example.com");
});
});

79
src/index.spec.ts Normal file
View File

@ -0,0 +1,79 @@
import { describe, it, expect, vi, beforeEach, afterEach, Mock } from "vitest";
import { Server } from "./Server";
vi.mock("@tsed/common", () => ({
$log: {
error: vi.fn()
},
PlatformApplication: vi.fn()
}));
vi.mock("@tsed/platform-express", () => ({
PlatformExpress: {
bootstrap: vi.fn()
}
}));
describe("bootstrap function", () => {
let bootstrap: () => Promise<void>;
let mockPlatform: {
listen: Mock;
stop: Mock;
};
beforeEach(async () => {
vi.clearAllMocks();
mockPlatform = {
listen: vi.fn().mockResolvedValue(undefined),
stop: vi.fn().mockResolvedValue(undefined)
};
const { PlatformExpress } = await import("@tsed/platform-express");
(PlatformExpress.bootstrap as Mock).mockResolvedValue(mockPlatform);
const module = await import("./index");
bootstrap = module.bootstrap;
});
afterEach(() => {
vi.restoreAllMocks();
});
it("should bootstrap the server successfully", async () => {
await bootstrap();
const { PlatformExpress } = await import("@tsed/platform-express");
expect(PlatformExpress.bootstrap).toHaveBeenCalledWith(Server);
expect(mockPlatform.listen).toHaveBeenCalled();
});
it("should handle errors during bootstrap", async () => {
const error = new Error("Bootstrap error");
const { PlatformExpress } = await import("@tsed/platform-express");
(PlatformExpress.bootstrap as Mock).mockRejectedValueOnce(error);
await bootstrap();
const { $log } = await import("@tsed/common");
expect($log.error).toHaveBeenCalledWith({
event: "SERVER_BOOTSTRAP_ERROR",
message: error.message,
stack: error.stack
});
});
it("should set up SIGINT handler", async () => {
const processOnSpy = vi.spyOn(process, "on");
await bootstrap();
expect(processOnSpy).toHaveBeenCalledWith("SIGINT", expect.any(Function));
// Simulate SIGINT
const sigintHandler = processOnSpy.mock.calls.find((call) => call[0] === "SIGINT")?.[1];
if (sigintHandler && typeof sigintHandler === "function") {
sigintHandler();
expect(mockPlatform.stop).toHaveBeenCalled();
} else {
throw new Error("SIGINT handler not found or not a function");
}
});
});

View File

@ -2,11 +2,10 @@ import { $log } from "@tsed/common";
import { PlatformExpress } from "@tsed/platform-express";
import { Server } from "./Server";
async function bootstrap() {
export async function bootstrap() {
try {
const platform = await PlatformExpress.bootstrap(Server);
await platform.listen();
process.on("SIGINT", () => {
platform.stop();
});
@ -15,4 +14,7 @@ async function bootstrap() {
}
}
// Only call bootstrap if this file is being run directly
if (require.main === module) {
bootstrap();
}

View File

@ -23,7 +23,8 @@
"resolveJsonModule": true,
"useDefineForClassFields": false,
"lib": ["es7", "dom", "ESNext.AsyncIterable"],
"typeRoots": ["./node_modules/@types"]
"typeRoots": ["./node_modules/@types"],
"noEmitHelpers": false
},
"include": ["src"],
"linterOptions": {

View File

@ -4,7 +4,10 @@ import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
root: "./"
root: "./",
coverage: {
exclude: ["./processes.config.js", "vitest.config.mts"]
}
},
plugins: [
// This is required to build the test files with SWC