mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-25 09:51:01 -05:00
Implemented HttpError
This commit is contained in:
parent
3f8d7d2115
commit
c566a4cfec
2 changed files with 202 additions and 0 deletions
86
src/utils/errors/http-error.ts
Normal file
86
src/utils/errors/http-error.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { HttpResponse } from "@/utils/net";
|
||||
import { SoftError } from "./soft-error";
|
||||
|
||||
/**
|
||||
* Represents an HTTP error.
|
||||
*/
|
||||
export class HttpError extends SoftError {
|
||||
/**
|
||||
* The HTTP Response object associated with this error.
|
||||
*/
|
||||
private readonly _response: HttpResponse;
|
||||
|
||||
/**
|
||||
* Creates a new {@link HttpError} instance.
|
||||
*
|
||||
* @param response - The HTTP Response object associated with this error.
|
||||
* @param message - The error message.
|
||||
* @param isSoft - Indicates whether the error is recoverable or not.
|
||||
*/
|
||||
constructor(response: HttpResponse, message?: string, isSoft?: boolean) {
|
||||
super(isSoft ?? isServerError(response), message);
|
||||
|
||||
this.name = "HttpError";
|
||||
this._response = response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTTP Response object associated with this error.
|
||||
*/
|
||||
get response(): HttpResponse {
|
||||
return this._response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts error information from the given HTTP Response object
|
||||
* and returns an {@link HttpError} instance.
|
||||
*
|
||||
* @param response - The HTTP Response object to extract the error information from.
|
||||
* @param isSoft - Indicates whether the error is recoverable or not.
|
||||
*
|
||||
* @returns A `Promise` that resolves to an {@link HttpError} instance.
|
||||
*/
|
||||
static async fromResponse(response: HttpResponse, isSoft?: boolean): Promise<HttpError> {
|
||||
const cachedResponse = HttpResponse.cache(response);
|
||||
const errorText = `${response.status} (${
|
||||
await cachedResponse.text()
|
||||
.then(x => x && !isHtmlDocument(x) ? `${response.statusText}, ${x}` : response.statusText)
|
||||
.catch(() => response.statusText)
|
||||
})`;
|
||||
|
||||
return new HttpError(cachedResponse, errorText, isSoft);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given error is an {@link HttpError}.
|
||||
*
|
||||
* @param error - The error to be checked.
|
||||
*
|
||||
* @returns `true` if the provided error is an instance of HttpError; otherwise, `false`.
|
||||
*/
|
||||
export function isHttpError(error: unknown): error is HttpError {
|
||||
return error instanceof HttpError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given `HttpResponse` represents a server error.
|
||||
*
|
||||
* @param response - The `HttpResponse` to check.
|
||||
*
|
||||
* @returns `true` if the response is a server error; otherwise, `false`.
|
||||
*/
|
||||
function isServerError(response: HttpResponse): boolean {
|
||||
return response && (response.status === 429 || response.status >= 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given text is an HTML document.
|
||||
*
|
||||
* @param text - The string to check.
|
||||
*
|
||||
* @returns `true` if the provided string is an HTML document; otherwise, `false`.
|
||||
*/
|
||||
function isHtmlDocument(text: string): boolean {
|
||||
return text.startsWith("<!DOCTYPE html");
|
||||
}
|
116
tests/unit/utils/errors.ts/http-error.spec.ts
Normal file
116
tests/unit/utils/errors.ts/http-error.spec.ts
Normal file
|
@ -0,0 +1,116 @@
|
|||
import { HttpResponse } from "@/utils/net/http-response";
|
||||
import { HttpError, isHttpError } from "@/utils/errors/http-error";
|
||||
|
||||
describe("HttpError", () => {
|
||||
describe("constructor", () => {
|
||||
test("initializes with given response and message", () => {
|
||||
const response = HttpResponse.text("Resource does not exist", { status: 404, statusText: "Not Found" });
|
||||
|
||||
const error = new HttpError(response, "An error occurred.", false);
|
||||
|
||||
expect(error).toBeInstanceOf(HttpError);
|
||||
expect(error.name).toBe("HttpError")
|
||||
expect(error.message).toBe("An error occurred.");
|
||||
expect(error.isSoft).toBe(false);
|
||||
expect(error.response).toBe(response);
|
||||
});
|
||||
|
||||
test("initializes with isSoft set to true for server error", () => {
|
||||
const response = HttpResponse.text("Somebody knows what happened?", { status: 500, statusText: "Internal Server Error" });
|
||||
|
||||
const error = new HttpError(response);
|
||||
|
||||
expect(error.isSoft).toBe(true);
|
||||
expect(error.response).toBe(response);
|
||||
});
|
||||
|
||||
test("initializes with isSoft set to false for client error", () => {
|
||||
const response = HttpResponse.text("It's not my fault!", { status: 400, statusText: "Bad Request" });
|
||||
|
||||
const error = new HttpError(response);
|
||||
|
||||
expect(error.isSoft).toBe(false);
|
||||
expect(error.response).toBe(response);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromResponse", () => {
|
||||
test("creates HttpError from given response", async () => {
|
||||
const response = HttpResponse.text("Resource does not exist", { status: 404, statusText: "Not Found" });
|
||||
|
||||
const error = await HttpError.fromResponse(response, false);
|
||||
|
||||
expect(error).toBeInstanceOf(HttpError);
|
||||
expect(error.name).toBe("HttpError")
|
||||
});
|
||||
|
||||
test("includes text content in the error message", async () => {
|
||||
const response = HttpResponse.json({ error: "Resource does not exist" }, { status: 404, statusText: "Not Found" });
|
||||
|
||||
const error = await HttpError.fromResponse(response, false);
|
||||
|
||||
expect(error.message).toBe(`404 (Not Found, ${JSON.stringify({ error: "Resource does not exist" })})`);
|
||||
});
|
||||
|
||||
test("does not include HTML content in the error message", async () => {
|
||||
const htmlContent = "<!DOCTYPE html><html><head><title>Not Found</title></head><body>Page Not Found</body></html>";
|
||||
const response = HttpResponse.text(htmlContent, { status: 404, statusText: "Not Found" });
|
||||
|
||||
const error = await HttpError.fromResponse(response);
|
||||
|
||||
expect(error.message).toBe("404 (Not Found)");
|
||||
});
|
||||
|
||||
test("returns soft error for server error codes", async () => {
|
||||
const response = HttpResponse.text("Somebody knows what happened?", { status: 500, statusText: "Internal Server Error" });
|
||||
|
||||
const error = await HttpError.fromResponse(response);
|
||||
|
||||
expect(error.isSoft).toBe(true);
|
||||
});
|
||||
|
||||
test("returns soft error for Too Many Requests error code (429)", async () => {
|
||||
const response = HttpResponse.text("", { status: 429, statusText: "Too Many Requests" });
|
||||
|
||||
const error = await HttpError.fromResponse(response);
|
||||
|
||||
expect(error.isSoft).toBe(true);
|
||||
});
|
||||
|
||||
test("returns non-soft error for client error codes", async () => {
|
||||
const response = HttpResponse.text("It's not my fault!", { status: 400, statusText: "Bad Request" });
|
||||
|
||||
const error = await HttpError.fromResponse(response);
|
||||
|
||||
expect(error.isSoft).toBe(false);
|
||||
});
|
||||
|
||||
test("can still read the response contents after the error is created", async () => {
|
||||
const response = HttpResponse.json({ error: "Resource does not exist" }, { status: 404, statusText: "Not Found" });
|
||||
|
||||
const error = await HttpError.fromResponse(response);
|
||||
const responseJson = await error.response.json();
|
||||
|
||||
expect(error.message).toBe(`404 (Not Found, ${JSON.stringify({ error: "Resource does not exist" })})`);
|
||||
expect(responseJson).toEqual({ error: "Resource does not exist" });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("isHttpError", () => {
|
||||
test("returns true for HttpError", () => {
|
||||
const response = HttpResponse.text("Resource does not exist", { status: 404, statusText: "Not Found" });
|
||||
|
||||
const error = new HttpError(response, "An error occurred.", false);
|
||||
|
||||
expect(isHttpError(error)).toBe(true);
|
||||
});
|
||||
|
||||
test("returns false for non-HttpError errors", () => {
|
||||
expect(isHttpError(new Error("An error occurred."))).toBe(false);
|
||||
});
|
||||
|
||||
test("returns false for non-error values", () => {
|
||||
expect(isHttpError("An error occurred.")).toBe(false);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue