mirror of
https://github.com/multipleof4/ntfy.git
synced 2026-10-11 06:15:21 +00:00
94 lines
3.2 KiB
JavaScript
94 lines
3.2 KiB
JavaScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
UnauthorizedError,
|
|
UserExistsError,
|
|
TopicReservedError,
|
|
AccountActionLimitReachedError,
|
|
IncorrectPasswordError,
|
|
EmailVerificationCodeInvalidError,
|
|
EmailPrimaryElsewhereError,
|
|
throwAppError,
|
|
fetchOrThrow,
|
|
} from "./errors";
|
|
|
|
// A minimal stand-in for a fetch Response: just the bits errors.js reads.
|
|
const fakeResponse = (status, body) => ({
|
|
status,
|
|
json: async () => {
|
|
if (body === undefined) throw new Error("no body");
|
|
return body;
|
|
},
|
|
});
|
|
|
|
beforeEach(() => {
|
|
// errors.js logs to console.log on every error path; keep test output clean.
|
|
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("error classes", () => {
|
|
it("carry the server error codes from errors.go", () => {
|
|
expect(UserExistsError.CODE).toBe(40901);
|
|
expect(TopicReservedError.CODE).toBe(40902);
|
|
expect(AccountActionLimitReachedError.CODE).toBe(42906);
|
|
expect(IncorrectPasswordError.CODE).toBe(40026);
|
|
expect(EmailVerificationCodeInvalidError.CODE).toBe(40051);
|
|
expect(EmailPrimaryElsewhereError.CODE).toBe(40908);
|
|
});
|
|
|
|
it("are Error subclasses with human-readable messages", () => {
|
|
expect(new UnauthorizedError()).toBeInstanceOf(Error);
|
|
expect(new UnauthorizedError().message).toBe("Unauthorized");
|
|
expect(new UserExistsError().message).toBe("Username already exists");
|
|
});
|
|
});
|
|
|
|
describe("throwAppError", () => {
|
|
it("maps 401 and 403 to UnauthorizedError", async () => {
|
|
await expect(throwAppError(fakeResponse(401))).rejects.toBeInstanceOf(UnauthorizedError);
|
|
await expect(throwAppError(fakeResponse(403))).rejects.toBeInstanceOf(UnauthorizedError);
|
|
});
|
|
|
|
it("maps known ntfy error codes to their specific error classes", async () => {
|
|
await expect(throwAppError(fakeResponse(409, { code: UserExistsError.CODE }))).rejects.toBeInstanceOf(UserExistsError);
|
|
await expect(throwAppError(fakeResponse(409, { code: TopicReservedError.CODE }))).rejects.toBeInstanceOf(TopicReservedError);
|
|
await expect(throwAppError(fakeResponse(429, { code: AccountActionLimitReachedError.CODE }))).rejects.toBeInstanceOf(
|
|
AccountActionLimitReachedError
|
|
);
|
|
});
|
|
|
|
it("wraps an unknown code that carries an error message in a generic Error", async () => {
|
|
await expect(throwAppError(fakeResponse(400, { code: 12345, error: "boom" }))).rejects.toThrow("Error 12345: boom");
|
|
});
|
|
|
|
it("throws a generic 'Unexpected response' error when there is no ntfy error body", async () => {
|
|
await expect(throwAppError(fakeResponse(500))).rejects.toThrow("Unexpected response 500");
|
|
});
|
|
});
|
|
|
|
describe("fetchOrThrow", () => {
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it("returns the response unchanged on HTTP 200", async () => {
|
|
const response = { status: 200 };
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () => response)
|
|
);
|
|
await expect(fetchOrThrow("https://ntfy.sh/mytopic/json")).resolves.toBe(response);
|
|
});
|
|
|
|
it("throws on a non-200 response", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () => fakeResponse(401))
|
|
);
|
|
await expect(fetchOrThrow("https://ntfy.sh/mytopic/json")).rejects.toBeInstanceOf(UnauthorizedError);
|
|
});
|
|
});
|