plan/react-rewrite #1
@@ -0,0 +1,59 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { errorToResponse } from "./map.js";
|
||||
import { ApiHttpError, ApiTimeoutError } from "@/shared/api/errors";
|
||||
|
||||
describe("errorToResponse", () => {
|
||||
it("maps ApiHttpError(404) to not_found", () => {
|
||||
const result = errorToResponse(new ApiHttpError("Not found", 404));
|
||||
expect(result).toEqual({ status: 404, errorCode: "not_found" });
|
||||
});
|
||||
|
||||
it("maps ApiHttpError(500) to internal", () => {
|
||||
const result = errorToResponse(new ApiHttpError("Server error", 500));
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
|
||||
it("maps ApiHttpError(502) to internal", () => {
|
||||
const result = errorToResponse(new ApiHttpError("Bad gateway", 502));
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
|
||||
it("maps ApiHttpError(503) to internal (5xx range)", () => {
|
||||
const result = errorToResponse(new ApiHttpError("Service unavailable", 503));
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
|
||||
it("maps ApiHttpError(599) to internal (5xx boundary)", () => {
|
||||
const result = errorToResponse(new ApiHttpError("Edge case", 599));
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
|
||||
it("maps ApiTimeoutError to unavailable with Retry-After header", () => {
|
||||
const result = errorToResponse(new ApiTimeoutError(5000));
|
||||
expect(result).toEqual({
|
||||
status: 503,
|
||||
headers: { "Retry-After": "30" },
|
||||
errorCode: "unavailable",
|
||||
});
|
||||
});
|
||||
|
||||
it("maps unknown Error to internal", () => {
|
||||
const result = errorToResponse(new Error("something broke"));
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
|
||||
it("maps non-Error value to internal", () => {
|
||||
const result = errorToResponse("string error");
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
|
||||
it("maps null to internal", () => {
|
||||
const result = errorToResponse(null);
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
|
||||
it("maps ApiHttpError with non-5xx/non-404 status to internal", () => {
|
||||
const result = errorToResponse(new ApiHttpError("Forbidden", 403));
|
||||
expect(result).toEqual({ status: 500, errorCode: "internal" });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ApiHttpError, ApiTimeoutError } from "@/shared/api/errors";
|
||||
|
||||
export interface ErrorResponse {
|
||||
status: 404 | 500 | 503;
|
||||
headers?: Record<string, string>;
|
||||
errorCode: "not_found" | "internal" | "unavailable";
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an application error to a structured HTTP-like response.
|
||||
* Consumed by SSR loader paths and error page routing.
|
||||
*
|
||||
* Mapping rules (design spec S4.6):
|
||||
* - ApiHttpError 404 -> { status: 404, errorCode: "not_found" }
|
||||
* - ApiHttpError 500-599 -> { status: 500, errorCode: "internal" }
|
||||
* - ApiTimeoutError -> { status: 503, Retry-After: 30, errorCode: "unavailable" }
|
||||
* - Everything else -> { status: 500, errorCode: "internal" }
|
||||
*/
|
||||
export function errorToResponse(error: unknown): ErrorResponse {
|
||||
if (error instanceof ApiHttpError) {
|
||||
if (error.status === 404) {
|
||||
return { status: 404, errorCode: "not_found" };
|
||||
}
|
||||
if (error.status >= 500 && error.status <= 599) {
|
||||
return { status: 500, errorCode: "internal" };
|
||||
}
|
||||
// Any other HTTP status (e.g. 403) falls through to the default
|
||||
return { status: 500, errorCode: "internal" };
|
||||
}
|
||||
|
||||
if (error instanceof ApiTimeoutError) {
|
||||
return {
|
||||
status: 503,
|
||||
headers: { "Retry-After": "30" },
|
||||
errorCode: "unavailable",
|
||||
};
|
||||
}
|
||||
|
||||
return { status: 500, errorCode: "internal" };
|
||||
}
|
||||
Reference in New Issue
Block a user