Skip to content

Commit

Permalink
feat: add unit tests for various utility functions and application li…
Browse files Browse the repository at this point in the history
…fecycle methods
  • Loading branch information
juliano-soares committed Nov 20, 2024
1 parent fd2551e commit d3d91b0
Show file tree
Hide file tree
Showing 24 changed files with 1,915 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Unit tests for: configureServices

import { ApplicationBase } from "../application-express.base";

class ConcreteApplication extends ApplicationBase {
private servicesConfigured: boolean = false;

protected async globalConfiguration(): Promise<void> {
}

protected async configureServices(): Promise<void> {
this.servicesConfigured = true;
}

protected async postServerInitialization(): Promise<void> {
}

protected async serverShutdown(): Promise<void> {
}

public isServicesConfigured(): boolean {
return this.servicesConfigured;
}
}

describe("ApplicationBase.configureServices() configureServices method", () => {
let app: ConcreteApplication;

beforeEach(() => {
app = new ConcreteApplication();
});

describe("Happy Path", () => {
it("should configure services successfully", async () => {
await (app as any).configureServices();
expect(app.isServicesConfigured()).toBe(true);
});
});

describe("Edge Cases", () => {
it("should handle repeated calls to configureServices gracefully", async () => {
await (app as any).configureServices();
await (app as any).configureServices();
expect(app.isServicesConfigured()).toBe(true);
});

it("should handle asynchronous operations correctly", async () => {
const configureServicesSpy = jest.spyOn(app as any, "configureServices");
await (app as any).configureServices();
expect(configureServicesSpy).toHaveBeenCalled();
});
});
});

// End of unit tests for: configureServices
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Unit tests for: globalConfiguration

import { ApplicationBase } from "../application-express.base";

class ConcreteApplication extends ApplicationBase {
private config: any;

constructor() {
super();
this.config = {};
}

protected async globalConfiguration(): Promise<void> {
this.config = { setting: "value" };
}

protected async configureServices(): Promise<void> {}
protected async postServerInitialization(): Promise<void> {}
protected async serverShutdown(): Promise<void> {}

public async callGlobalConfiguration(): Promise<void> {
await this.globalConfiguration();
}

public getConfig(): any {
return this.config;
}
}

describe("ApplicationBase.globalConfiguration() globalConfiguration method", () => {
let app: ConcreteApplication;

beforeEach(() => {
app = new ConcreteApplication();
});

describe("Happy Path", () => {
it("should set the global configuration correctly", async () => {
// Access indirectly via public method
await app.callGlobalConfiguration();
expect(app.getConfig()).toEqual({ setting: "value" });
});
});

describe("Edge Cases", () => {
it("should handle asynchronous operations correctly", async () => {
const asyncConfigApp = new (class extends ConcreteApplication {
protected async globalConfiguration(): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
this["config"] = { asyncSetting: "asyncValue" }; // Access private attribute indirectly
resolve();
}, 100);
});
}
})();

await asyncConfigApp.callGlobalConfiguration();
expect(asyncConfigApp.getConfig()).toEqual({ asyncSetting: "asyncValue" });
});

it("should handle empty configuration gracefully", async () => {
const emptyConfigApp = new (class extends ConcreteApplication {
protected async globalConfiguration(): Promise<void> {
this["config"] = {}; // Access private attribute indirectly
}
})();

await emptyConfigApp.callGlobalConfiguration();
expect(emptyConfigApp.getConfig()).toEqual({});
});
});
});

// End of unit tests for: globalConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Unit tests for: postServerInitialization

import { ApplicationBase } from "../application-express.base";

class ConcreteApplication extends ApplicationBase {
protected globalConfiguration(): void | Promise<void> {}
protected configureServices(): void | Promise<void> {}

protected async postServerInitialization(): Promise<void> {
console.log("Server initialized");
}

protected serverShutdown(): void | Promise<void> {}

public async callPostServerInitialization(): Promise<void> {
await this.postServerInitialization();
}
}

describe("ApplicationBase.postServerInitialization() postServerInitialization method", () => {
let app: ConcreteApplication;

beforeEach(() => {
app = new ConcreteApplication();
});

describe("Happy Path", () => {
it("should execute postServerInitialization without errors", async () => {
await expect(app.callPostServerInitialization()).resolves.toBeUndefined();
});
});

describe("Edge Cases", () => {
it("should handle asynchronous operations correctly", async () => {
jest.spyOn(app as any, "postServerInitialization").mockResolvedValueOnce(undefined);
await expect(app.callPostServerInitialization()).resolves.toBeUndefined();
});

it("should handle errors thrown within the method", async () => {
const error = new Error("Initialization error");
jest.spyOn(app as any, "postServerInitialization").mockRejectedValueOnce(error);
await expect(app.callPostServerInitialization()).rejects.toThrow("Initialization error");
});
});
});

// End of unit tests for: postServerInitialization
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Unit tests for: serverShutdown

import { ApplicationBase } from "../application-express.base";

export class ConcreteApplication extends ApplicationBase {
protected globalConfiguration(): void | Promise<void> {}
protected configureServices(): void | Promise<void> {}
protected postServerInitialization(): void | Promise<void> {}

protected async serverShutdown(): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 100);
});
}

public async callServerShutdown(): Promise<void> {
return this.serverShutdown();
}
}

describe("ApplicationBase.serverShutdown() serverShutdown method", () => {
let app: ConcreteApplication;

beforeEach(() => {
app = new ConcreteApplication();
});

describe("Happy Path", () => {
it("should resolve the promise indicating successful shutdown", async () => {
await expect(app.callServerShutdown()).resolves.toBeUndefined();
});
});

describe("Edge Cases", () => {
it("should handle immediate resolution without delay", async () => {
jest.spyOn(app as any, "serverShutdown").mockResolvedValueOnce(undefined);
await expect(app.callServerShutdown()).resolves.toBeUndefined();
});

it("should handle rejection gracefully", async () => {
jest.spyOn(app as any, "serverShutdown").mockRejectedValueOnce(new Error("Shutdown failed"));
await expect(app.callServerShutdown()).rejects.toThrow("Shutdown failed");
});
});
});

// End of unit tests for: serverShutdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Unit tests for: configContainer

import { interfaces } from "../../di/di.interfaces";
import { AppExpress } from "../application-express";

jest.mock("@expressots/core", () => ({
ExpressoMiddleware: class {},
AppContainer: jest.fn().mockImplementation(() => new MockAppContainer() as any),
Logger: jest.fn().mockImplementation(() => new MockLogger() as any),
ProviderManager: jest.fn().mockImplementation(() => new MockProviderManager() as any),
Middleware: jest.fn().mockImplementation(() => new MockMiddleware() as any),
Console: jest.fn().mockImplementation(() => new MockConsole() as any),
injectable: () => (target: any) => target,
inject: jest.fn(() => jest.fn()),
}));

class MockLogger {
error = jest.fn();
}

class MockProviderManager {
constructor() {}
}

class MockMiddleware {
getMiddlewarePipeline = jest.fn().mockReturnValue([]);
}

class MockAppContainer {
Container = {};
create = jest.fn();
}

class MockConsole {
messageServer = jest.fn();
}

describe("AppExpress.configContainer() configContainer method", () => {
let appExpress: AppExpress;

beforeEach(() => {
appExpress = new AppExpress();
});

describe("Happy Path", () => {
it("should configure the container with provided modules", () => {
const mockModules: Array<interfaces.ContainerModule> = [{} as any];
const mockOptions: interfaces.ContainerOptions = {} as any;

const result = appExpress.configContainer(mockModules, mockOptions);

expect(result).toBeInstanceOf(MockAppContainer);
expect(result.create).toHaveBeenCalledWith(mockModules);
});

it("should configure the container with default options if none are provided", () => {
const mockModules: Array<interfaces.ContainerModule> = [{} as any];

const result = appExpress.configContainer(mockModules);

expect(result).toBeInstanceOf(MockAppContainer);
expect(result.create).toHaveBeenCalledWith(mockModules);
});
});

describe("Edge Cases", () => {
it("should log an error if no modules are provided", () => {
const mockLogger = new MockLogger();
appExpress["logger"] = mockLogger as any;

const result = appExpress.configContainer(undefined as any);

expect(result).toBeUndefined();
expect(mockLogger.error).toHaveBeenCalledWith(
"No modules provided for container configuration",
"adapter-express",
);
});
});
});

// End of unit tests for: configContainer
Loading

0 comments on commit d3d91b0

Please sign in to comment.