refactor(errors): classify auth and browser failures
This commit is contained in:
@@ -5,6 +5,7 @@ import memoryDriver from "unstorage/drivers/memory";
|
||||
import { AuthStorage } from "@/data/storage/auth";
|
||||
import { SpAsyncUtil } from "@/utils";
|
||||
|
||||
import { ErrorCode } from "../api_result";
|
||||
import { createHttpClient } from "../http_client";
|
||||
|
||||
function jsonResponse(body: unknown, init?: ResponseInit): Response {
|
||||
@@ -86,4 +87,61 @@ describe("createHttpClient", () => {
|
||||
"Bearer new-login-token",
|
||||
]);
|
||||
});
|
||||
|
||||
it("rejects with an API unauthorized error when no refresh token exists", async () => {
|
||||
const authStorage = AuthStorage.getInstance();
|
||||
await authStorage.setLoginToken("old-login-token");
|
||||
|
||||
const fetchMock = vi.fn<typeof fetch>(async () =>
|
||||
jsonResponse(
|
||||
{ message: "expired" },
|
||||
{ status: ErrorCode.httpUnauthorized, statusText: "Unauthorized" },
|
||||
),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const http = createHttpClient();
|
||||
await expect(http("/api/chat/history")).rejects.toMatchObject({
|
||||
name: "ApiError",
|
||||
code: "HTTP_UNAUTHORIZED",
|
||||
status: ErrorCode.httpUnauthorized,
|
||||
message: "No refresh token available",
|
||||
});
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("rejects with API status context when token refresh fails", async () => {
|
||||
const authStorage = AuthStorage.getInstance();
|
||||
await authStorage.setLoginToken("old-login-token");
|
||||
await authStorage.setRefreshToken("refresh-token");
|
||||
|
||||
const fetchMock = vi.fn<typeof fetch>(async (input) => {
|
||||
const url = new URL(input instanceof Request ? input.url : String(input));
|
||||
if (url.pathname === "/api/auth/refresh") {
|
||||
return jsonResponse(
|
||||
{ message: "refresh broken" },
|
||||
{ status: ErrorCode.httpServerError, statusText: "Server Error" },
|
||||
);
|
||||
}
|
||||
return jsonResponse(
|
||||
{ message: "expired" },
|
||||
{ status: ErrorCode.httpUnauthorized, statusText: "Unauthorized" },
|
||||
);
|
||||
});
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const http = createHttpClient();
|
||||
await expect(http("/api/chat/history")).rejects.toMatchObject({
|
||||
name: "ApiError",
|
||||
code: "HTTP_SERVER_ERROR",
|
||||
status: ErrorCode.httpServerError,
|
||||
message: "Refresh failed",
|
||||
});
|
||||
|
||||
expect(fetchMock.mock.calls.map((call) => callUrl(call).pathname)).toEqual([
|
||||
"/api/chat/history",
|
||||
"/api/auth/refresh",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user