267 lines
9.6 KiB
TypeScript
267 lines
9.6 KiB
TypeScript
import { describe, test, expect } from "bun:test";
|
|
import GenericError from "../../src/Errors/GenericError";
|
|
import AuthenticationError from "../../src/Errors/AuthenticationError";
|
|
import AuthorizationError from "../../src/Errors/AuthorizationError";
|
|
import BodyError from "../../src/Errors/BodyError";
|
|
import InsufficientPermission from "../../src/Errors/InsufficientPermission";
|
|
import SessionError from "../../src/Errors/SessionError";
|
|
import SessionTokenError from "../../src/Errors/SessionTokenError";
|
|
import UserError from "../../src/Errors/UserError";
|
|
import RoleError from "../../src/Errors/RoleError";
|
|
import MissingBodyValue from "../../src/Errors/MissingBodyValue";
|
|
import ExpiredAccessTokenError from "../../src/Errors/ExpiredAccessTokenError";
|
|
import ExpiredRefreshTokenError from "../../src/Errors/ExpiredRefreshTokenError";
|
|
import PermissionsVerificationError from "../../src/Errors/PermissionsVerificationError";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// GenericError
|
|
// ---------------------------------------------------------------------------
|
|
describe("GenericError", () => {
|
|
test("sets name, message, status, and cause", () => {
|
|
const err = new GenericError({
|
|
name: "TestError",
|
|
message: "Something went wrong",
|
|
cause: "bad input",
|
|
status: 422,
|
|
});
|
|
expect(err).toBeInstanceOf(Error);
|
|
expect(err.name).toBe("TestError");
|
|
expect(err.message).toBe("Something went wrong");
|
|
expect(err.cause).toBe("bad input");
|
|
expect(err.status).toBe(422);
|
|
});
|
|
|
|
test("defaults status to 400", () => {
|
|
const err = new GenericError({ name: "X", message: "Y" });
|
|
expect(err.status).toBe(400);
|
|
});
|
|
|
|
test("cause is optional", () => {
|
|
const err = new GenericError({ name: "X", message: "Y" });
|
|
expect(err.cause).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// AuthenticationError
|
|
// ---------------------------------------------------------------------------
|
|
describe("AuthenticationError", () => {
|
|
test("sets correct name and message", () => {
|
|
const err = new AuthenticationError("Invalid credentials");
|
|
expect(err).toBeInstanceOf(Error);
|
|
expect(err.name).toBe("AuthenticationError");
|
|
expect(err.message).toBe("Invalid credentials");
|
|
});
|
|
|
|
test("accepts optional cause", () => {
|
|
const err = new AuthenticationError("Fail", "token expired");
|
|
expect(err.cause).toBe("token expired");
|
|
});
|
|
|
|
test("cause defaults to undefined", () => {
|
|
const err = new AuthenticationError("Fail");
|
|
expect(err.cause).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// AuthorizationError
|
|
// ---------------------------------------------------------------------------
|
|
describe("AuthorizationError", () => {
|
|
test("sets correct name and default status", () => {
|
|
const err = new AuthorizationError("Not authorized");
|
|
expect(err).toBeInstanceOf(Error);
|
|
expect(err.name).toBe("AuthorizationError");
|
|
expect(err.message).toBe("Not authorized");
|
|
expect(err.status).toBe(401);
|
|
});
|
|
|
|
test("allows custom status", () => {
|
|
const err = new AuthorizationError("Forbidden", "nope", 403);
|
|
expect(err.status).toBe(403);
|
|
expect(err.cause).toBe("nope");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// BodyError
|
|
// ---------------------------------------------------------------------------
|
|
describe("BodyError", () => {
|
|
test("sets name and message", () => {
|
|
const err = new BodyError("Body is invalid");
|
|
expect(err.name).toBe("BodyError");
|
|
expect(err.message).toBe("Body is invalid");
|
|
});
|
|
|
|
test("accepts optional cause", () => {
|
|
const err = new BodyError("Bad", "missing field");
|
|
expect(err.cause).toBe("missing field");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// InsufficientPermission
|
|
// ---------------------------------------------------------------------------
|
|
describe("InsufficientPermission", () => {
|
|
test("always has status 403", () => {
|
|
const err = new InsufficientPermission("Nope");
|
|
expect(err.name).toBe("InsufficientPermission");
|
|
expect(err.status).toBe(403);
|
|
expect(err.message).toBe("Nope");
|
|
});
|
|
|
|
test("accepts optional cause", () => {
|
|
const err = new InsufficientPermission("Nope", "missing role");
|
|
expect(err.cause).toBe("missing role");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SessionError
|
|
// ---------------------------------------------------------------------------
|
|
describe("SessionError", () => {
|
|
test("sets name and message", () => {
|
|
const err = new SessionError("Invalid session");
|
|
expect(err.name).toBe("SessionError");
|
|
expect(err.message).toBe("Invalid session");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SessionTokenError
|
|
// ---------------------------------------------------------------------------
|
|
describe("SessionTokenError", () => {
|
|
test("sets name and message", () => {
|
|
const err = new SessionTokenError("Token invalid");
|
|
expect(err.name).toBe("SessionTokenError");
|
|
expect(err.message).toBe("Token invalid");
|
|
});
|
|
|
|
test("accepts cause", () => {
|
|
const err = new SessionTokenError("Bad", "expired");
|
|
expect(err.cause).toBe("expired");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// UserError
|
|
// ---------------------------------------------------------------------------
|
|
describe("UserError", () => {
|
|
test("sets name and message", () => {
|
|
const err = new UserError("User not found");
|
|
expect(err.name).toBe("UserError");
|
|
expect(err.message).toBe("User not found");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// RoleError
|
|
// ---------------------------------------------------------------------------
|
|
describe("RoleError", () => {
|
|
test("sets name and message", () => {
|
|
const err = new RoleError("Role conflict");
|
|
expect(err.name).toBe("RoleError");
|
|
expect(err.message).toBe("Role conflict");
|
|
});
|
|
|
|
test("accepts cause", () => {
|
|
const err = new RoleError("Conflict", "moniker taken");
|
|
expect(err.cause).toBe("moniker taken");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// MissingBodyValue
|
|
// ---------------------------------------------------------------------------
|
|
describe("MissingBodyValue", () => {
|
|
test("formats message with value name", () => {
|
|
const err = new MissingBodyValue("email");
|
|
expect(err.name).toBe("MissingBodyValue");
|
|
expect(err.message).toBe("Value 'email' is missing from the body.");
|
|
expect(err.cause).toBe(
|
|
"A value that was required by the body of this request is missing.",
|
|
);
|
|
});
|
|
|
|
test("works with different value names", () => {
|
|
const err = new MissingBodyValue("password");
|
|
expect(err.message).toContain("password");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ExpiredAccessTokenError
|
|
// ---------------------------------------------------------------------------
|
|
describe("ExpiredAccessTokenError", () => {
|
|
test("sets fixed name and message", () => {
|
|
const err = new ExpiredAccessTokenError();
|
|
expect(err.name).toBe("ExpiredAccessTokenError");
|
|
expect(err.message).toBe("The provided access token has expired.");
|
|
});
|
|
|
|
test("accepts optional cause", () => {
|
|
const err = new ExpiredAccessTokenError("jwt expired");
|
|
expect(err.cause).toBe("jwt expired");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ExpiredRefreshTokenError
|
|
// ---------------------------------------------------------------------------
|
|
describe("ExpiredRefreshTokenError", () => {
|
|
test("sets fixed name and message", () => {
|
|
const err = new ExpiredRefreshTokenError();
|
|
expect(err.name).toBe("ExpiredRefreshTokenError");
|
|
expect(err.message).toBe("The provided refresh token has expired.");
|
|
});
|
|
|
|
test("accepts optional cause", () => {
|
|
const err = new ExpiredRefreshTokenError("jwt expired");
|
|
expect(err.cause).toBe("jwt expired");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// PermissionsVerificationError
|
|
// ---------------------------------------------------------------------------
|
|
describe("PermissionsVerificationError", () => {
|
|
test("sets name and message", () => {
|
|
const err = new PermissionsVerificationError("Cannot verify");
|
|
expect(err.name).toBe("PermissionsVerificationError");
|
|
expect(err.message).toBe("Cannot verify");
|
|
});
|
|
|
|
test("accepts cause", () => {
|
|
const err = new PermissionsVerificationError("Fail", "key mismatch");
|
|
expect(err.cause).toBe("key mismatch");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Cross-cutting: all errors are instanceof Error
|
|
// ---------------------------------------------------------------------------
|
|
describe("All errors extend Error", () => {
|
|
const errors = [
|
|
new GenericError({ name: "G", message: "g" }),
|
|
new AuthenticationError("a"),
|
|
new AuthorizationError("a"),
|
|
new BodyError("b"),
|
|
new InsufficientPermission("i"),
|
|
new SessionError("s"),
|
|
new SessionTokenError("st"),
|
|
new UserError("u"),
|
|
new RoleError("r"),
|
|
new MissingBodyValue("v"),
|
|
new ExpiredAccessTokenError(),
|
|
new ExpiredRefreshTokenError(),
|
|
new PermissionsVerificationError("p"),
|
|
];
|
|
|
|
test.each(errors.map((e) => [e.constructor.name, e]))(
|
|
"%s is instanceof Error",
|
|
(_name, err) => {
|
|
expect(err).toBeInstanceOf(Error);
|
|
},
|
|
);
|
|
});
|