refactor(errors): classify auth and browser failures

This commit is contained in:
2026-07-03 13:20:16 +08:00
parent 8a71041b5d
commit 8a586e4471
7 changed files with 177 additions and 9 deletions
@@ -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",
]);
});
});