Implemented ErrorBuilder class

This commit is contained in:
Kir_Antipov 2023-05-11 14:22:01 +00:00
parent 20e88826d4
commit 1d8d9fdb7c
3 changed files with 171 additions and 0 deletions

View file

@ -0,0 +1,77 @@
import { Logger, NULL_LOGGER } from "@/utils/logging";
import { FailMode } from "./fail-mode";
/**
* A class for building and handling errors based on a given mode.
*/
export class ErrorBuilder {
/**
* The logger to use for logging errors.
*/
private readonly _logger: Logger;
/**
* The accumulated errors.
*/
private readonly _errors: Error[];
/**
* Constructs a new {@link ErrorBuilder} instance.
*
* @param logger - The logger to use for logging errors.
*/
constructor(logger?: Logger) {
this._logger = logger || NULL_LOGGER;
this._errors = [];
}
/**
* Checks if any errors have been appended.
*
* @returns `true` if there are errors; otherwise, `false`.
*/
get hasErrors(): boolean {
return this._errors.length > 0;
}
/**
* Appends an error to the builder, handling it according to the provided mode.
*
* @param error - The error to append.
* @param mode - The mode to use when handling the error. Defaults to `SKIP` if not provided.
*/
append(error: Error, mode?: FailMode): void {
switch (mode ?? FailMode.SKIP) {
case FailMode.WARN:
this._logger.error(error);
break;
case FailMode.SKIP:
this._logger.error(error);
this._errors.push(error);
break;
default:
throw error;
}
}
/**
* Builds an `AggregateError` from the errors appended so far.
*
* @returns The built error, or `undefined` if no errors have been appended.
*/
build(): Error | undefined {
return this.hasErrors ? new AggregateError(this._errors) : undefined;
}
/**
* Builds an `AggregateError` from the errors appended so far, and throw it.
*
* @throws The built error, if any errors have been appended.
*/
throwIfHasErrors(): void | never {
const error = this.build();
if (error) {
throw error;
}
}
}

View file

@ -1,6 +1,7 @@
export { isError } from "./error";
export { ArgumentError } from "./argument-error";
export { ArgumentNullError } from "./argument-null-error";
export { ErrorBuilder } from "./error-builder";
export { FailMode } from "./fail-mode";
export { SoftError, isSoftError } from "./soft-error";
export { FileNotFoundError } from "./file-not-found-error";

View file

@ -0,0 +1,93 @@
import { FailMode } from "@/utils/errors/fail-mode";
import { Logger } from "@/utils/logging/logger";
import { ErrorBuilder } from "@/utils/errors/error-builder";
describe("ErrorBuilder", () => {
describe("hasErrors", () => {
test("returns false if no errors were appended to the builder", () => {
const builder = new ErrorBuilder();
expect(builder.hasErrors).toBe(false);
});
test("returns true if errors were appended to the builder", () => {
const builder = new ErrorBuilder();
builder.append(new Error());
expect(builder.hasErrors).toBe(true);
});
});
describe("append", () => {
it("appends an error and logs it when mode is SKIP", () => {
const logger = { error: jest.fn() } as unknown as Logger;
const builder = new ErrorBuilder(logger);
const error = new Error("Test error");
builder.append(error, FailMode.SKIP);
expect(logger.error).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledWith(error);
expect(builder.hasErrors).toBe(true);
});
it("processes an error and logs it when mode is WARN", () => {
const logger = { error: jest.fn() } as unknown as Logger;
const builder = new ErrorBuilder(logger);
const error = new Error("Test error");
builder.append(error, FailMode.WARN);
expect(logger.error).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledWith(error);
expect(builder.hasErrors).toBe(false);
});
it("throws error immediately when mode is FAIL", () => {
const logger = { error: jest.fn() } as unknown as Logger;
const builder = new ErrorBuilder(logger);
const error = new Error("Test error");
expect(() => builder.append(error, FailMode.FAIL)).toThrow(error);
expect(logger.error).not.toHaveBeenCalled();
expect(builder.hasErrors).toBe(false);
});
});
describe("build", () => {
it("returns undefined when there are no errors", () => {
const builder = new ErrorBuilder();
expect(builder.build()).toBeUndefined();
});
it("returns an AggregateError when there are errors", () => {
const error = new Error("Test error");
const builder = new ErrorBuilder();
builder.append(error);
const builtError = builder.build();
expect(builtError).toBeInstanceOf(AggregateError);
expect((builtError as AggregateError).errors).toEqual([error]);
});
});
describe("throwIfHasErrors", () => {
it("does not throw when there are no errors", () => {
const builder = new ErrorBuilder();
expect(() => builder.throwIfHasErrors()).not.toThrow();
});
it("throws an AggregateError when there are errors", () => {
const error = new Error("Test error");
const builder = new ErrorBuilder();
builder.append(error);
expect(() => builder.throwIfHasErrors()).toThrow(new AggregateError([error]));
});
});
});