mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-25 09:51:01 -05:00
Implemented ErrorBuilder
class
This commit is contained in:
parent
20e88826d4
commit
1d8d9fdb7c
3 changed files with 171 additions and 0 deletions
77
src/utils/errors/error-builder.ts
Normal file
77
src/utils/errors/error-builder.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
export { isError } from "./error";
|
export { isError } from "./error";
|
||||||
export { ArgumentError } from "./argument-error";
|
export { ArgumentError } from "./argument-error";
|
||||||
export { ArgumentNullError } from "./argument-null-error";
|
export { ArgumentNullError } from "./argument-null-error";
|
||||||
|
export { ErrorBuilder } from "./error-builder";
|
||||||
export { FailMode } from "./fail-mode";
|
export { FailMode } from "./fail-mode";
|
||||||
export { SoftError, isSoftError } from "./soft-error";
|
export { SoftError, isSoftError } from "./soft-error";
|
||||||
export { FileNotFoundError } from "./file-not-found-error";
|
export { FileNotFoundError } from "./file-not-found-error";
|
||||||
|
|
93
tests/unit/utils/errors/error-builder.spec.ts
Normal file
93
tests/unit/utils/errors/error-builder.spec.ts
Normal 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]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue