paperclip-app / packages /shared /src /__tests__ /postiz-errors.test.ts
github-actions[bot]
deploy 2026-04-24T08:06
8523e75
Raw
History Blame Contribute Delete
11 kB
import { describe, it, expect } from "vitest";
import {
PostizErrorCode,
PostizError,
PostizAuthError,
PostizRateLimitError,
PostizNotFoundError,
PostizValidationFormatError,
PostizPlatformError,
PostizNetworkError,
PostizTimeoutError,
PostizServiceUnavailableError,
isPostizError,
isRecoverableError,
} from "../types/postiz-errors.js";
describe("PostizError", () => {
describe("PostizErrorCode enum", () => {
it("should have all expected error codes", () => {
expect(PostizErrorCode.AUTH_ERROR).toBe("AUTH_ERROR");
expect(PostizErrorCode.RATE_LIMIT).toBe("RATE_LIMIT");
expect(PostizErrorCode.NOT_FOUND).toBe("NOT_FOUND");
expect(PostizErrorCode.VALIDATION_ERROR).toBe("VALIDATION_ERROR");
expect(PostizErrorCode.PLATFORM_ERROR).toBe("PLATFORM_ERROR");
expect(PostizErrorCode.NETWORK_ERROR).toBe("NETWORK_ERROR");
expect(PostizErrorCode.TIMEOUT_ERROR).toBe("TIMEOUT_ERROR");
expect(PostizErrorCode.SERVICE_UNAVAILABLE).toBe("SERVICE_UNAVAILABLE");
expect(PostizErrorCode.UNKNOWN_ERROR).toBe("UNKNOWN_ERROR");
});
});
describe("PostizError base class", () => {
it("should create error with all properties", () => {
const details = { field: "test" };
const error = new PostizError(
"Test error",
PostizErrorCode.UNKNOWN_ERROR,
500,
details,
true,
60
);
expect(error.message).toBe("Test error");
expect(error.name).toBe("PostizError");
expect(error.code).toBe(PostizErrorCode.UNKNOWN_ERROR);
expect(error.statusCode).toBe(500);
expect(error.details).toEqual(details);
expect(error.recoverable).toBe(true);
expect(error.retryAfter).toBe(60);
});
it("should have default recoverable value of false", () => {
const error = new PostizError(
"Test error",
PostizErrorCode.UNKNOWN_ERROR,
500
);
expect(error.recoverable).toBe(false);
expect(error.retryAfter).toBeUndefined();
});
it("should be an instance of Error", () => {
const error = new PostizError(
"Test error",
PostizErrorCode.UNKNOWN_ERROR,
500
);
expect(error).toBeInstanceOf(Error);
expect(error).toBeInstanceOf(PostizError);
});
});
describe("PostizAuthError", () => {
it("should create error with correct properties", () => {
const error = new PostizAuthError("Invalid API key");
expect(error.message).toBe("Invalid API key");
expect(error.name).toBe("PostizAuthError");
expect(error.code).toBe(PostizErrorCode.AUTH_ERROR);
expect(error.statusCode).toBe(401);
expect(error.recoverable).toBe(false);
});
it("should have default message", () => {
const error = new PostizAuthError();
expect(error.message).toBe("Authentication failed");
});
it("should include details when provided", () => {
const details = { reason: "expired" };
const error = new PostizAuthError("Token expired", details);
expect(error.details).toEqual(details);
});
});
describe("PostizRateLimitError", () => {
it("should create error with correct properties", () => {
const error = new PostizRateLimitError("Rate limit exceeded", 120);
expect(error.message).toBe("Rate limit exceeded");
expect(error.name).toBe("PostizRateLimitError");
expect(error.code).toBe(PostizErrorCode.RATE_LIMIT);
expect(error.statusCode).toBe(429);
expect(error.recoverable).toBe(true);
expect(error.retryAfter).toBe(120);
});
it("should have default values", () => {
const error = new PostizRateLimitError();
expect(error.message).toBe("Rate limit exceeded");
expect(error.retryAfter).toBe(60);
});
it("should allow custom retryAfter value", () => {
const error = new PostizRateLimitError("Slow down", 300);
expect(error.retryAfter).toBe(300);
});
});
describe("PostizNotFoundError", () => {
it("should create error with resource name only", () => {
const error = new PostizNotFoundError("Post");
expect(error.message).toBe("Post not found");
expect(error.name).toBe("PostizNotFoundError");
expect(error.code).toBe(PostizErrorCode.NOT_FOUND);
expect(error.statusCode).toBe(404);
expect(error.recoverable).toBe(false);
});
it("should create error with resource name and identifier", () => {
const error = new PostizNotFoundError("Post", "abc123");
expect(error.message).toBe("Post not found: abc123");
});
});
describe("PostizValidationFormatError", () => {
it("should create error with correct properties", () => {
const validationErrors = [{ field: "content", message: "Required" }];
const error = new PostizValidationFormatError(
"Validation failed",
validationErrors
);
expect(error.message).toBe("Validation failed");
expect(error.name).toBe("PostizValidationFormatError");
expect(error.code).toBe(PostizErrorCode.VALIDATION_ERROR);
expect(error.statusCode).toBe(400);
expect(error.recoverable).toBe(false);
expect(error.validationErrors).toEqual(validationErrors);
});
it("should work without validation errors", () => {
const error = new PostizValidationFormatError("Invalid format");
expect(error.validationErrors).toBeUndefined();
});
});
describe("PostizPlatformError", () => {
it("should create error with correct properties", () => {
const error = new PostizPlatformError(
"twitter",
"Character limit exceeded",
"CHAR_LIMIT"
);
expect(error.message).toBe("Character limit exceeded");
expect(error.name).toBe("PostizPlatformError");
expect(error.code).toBe(PostizErrorCode.PLATFORM_ERROR);
expect(error.statusCode).toBe(400);
expect(error.recoverable).toBe(false);
expect(error.platform).toBe("twitter");
expect(error.platformCode).toBe("CHAR_LIMIT");
});
it("should work without platform code", () => {
const error = new PostizPlatformError("instagram", "Media not supported");
expect(error.platform).toBe("instagram");
expect(error.platformCode).toBeUndefined();
});
});
describe("PostizNetworkError", () => {
it("should create error with correct properties", () => {
const error = new PostizNetworkError("Connection refused");
expect(error.message).toBe("Connection refused");
expect(error.name).toBe("PostizNetworkError");
expect(error.code).toBe(PostizErrorCode.NETWORK_ERROR);
expect(error.statusCode).toBe(0);
expect(error.recoverable).toBe(true);
});
it("should have default message", () => {
const error = new PostizNetworkError();
expect(error.message).toBe("Network error");
});
it("should include details when provided", () => {
const details = { originalError: new Error("ECONNREFUSED") };
const error = new PostizNetworkError("Connection failed", details);
expect(error.details).toEqual(details);
});
});
describe("PostizTimeoutError", () => {
it("should create error with correct properties", () => {
const error = new PostizTimeoutError("Request timed out after 30s");
expect(error.message).toBe("Request timed out after 30s");
expect(error.name).toBe("PostizTimeoutError");
expect(error.code).toBe(PostizErrorCode.TIMEOUT_ERROR);
expect(error.statusCode).toBe(504);
expect(error.recoverable).toBe(true);
});
it("should have default message", () => {
const error = new PostizTimeoutError();
expect(error.message).toBe("Request timeout");
});
});
describe("PostizServiceUnavailableError", () => {
it("should create error with correct properties", () => {
const error = new PostizServiceUnavailableError("Service is down");
expect(error.message).toBe("Service is down");
expect(error.name).toBe("PostizServiceUnavailableError");
expect(error.code).toBe(PostizErrorCode.SERVICE_UNAVAILABLE);
expect(error.statusCode).toBe(503);
expect(error.recoverable).toBe(true);
});
it("should have default message", () => {
const error = new PostizServiceUnavailableError();
expect(error.message).toBe("Service unavailable");
});
});
});
describe("Helper functions", () => {
describe("isPostizError", () => {
it("should return true for PostizError instances", () => {
const error = new PostizError(
"Test",
PostizErrorCode.UNKNOWN_ERROR,
500
);
expect(isPostizError(error)).toBe(true);
});
it("should return true for subclass instances", () => {
expect(isPostizError(new PostizAuthError())).toBe(true);
expect(isPostizError(new PostizRateLimitError())).toBe(true);
expect(isPostizError(new PostizNotFoundError("Test"))).toBe(true);
expect(isPostizError(new PostizNetworkError())).toBe(true);
});
it("should return false for regular Error", () => {
const error = new Error("Test error");
expect(isPostizError(error)).toBe(false);
});
it("should return false for non-error values", () => {
expect(isPostizError(null)).toBe(false);
expect(isPostizError(undefined)).toBe(false);
expect(isPostizError("error")).toBe(false);
expect(isPostizError({})).toBe(false);
});
});
describe("isRecoverableError", () => {
it("should return true for recoverable PostizError", () => {
expect(isRecoverableError(new PostizRateLimitError())).toBe(true);
expect(isRecoverableError(new PostizNetworkError())).toBe(true);
expect(isRecoverableError(new PostizTimeoutError())).toBe(true);
expect(isRecoverableError(new PostizServiceUnavailableError())).toBe(true);
});
it("should return false for non-recoverable PostizError", () => {
expect(isRecoverableError(new PostizAuthError())).toBe(false);
expect(isRecoverableError(new PostizNotFoundError("Test"))).toBe(false);
expect(isRecoverableError(new PostizValidationFormatError("Test"))).toBe(false);
expect(isRecoverableError(new PostizPlatformError("x", "Test"))).toBe(false);
});
it("should return false for regular Error", () => {
const error = new Error("Test error");
expect(isRecoverableError(error)).toBe(false);
});
it("should return false for non-error values", () => {
expect(isRecoverableError(null)).toBe(false);
expect(isRecoverableError(undefined)).toBe(false);
expect(isRecoverableError("error")).toBe(false);
});
it("should return true for custom PostizError with recoverable=true", () => {
const error = new PostizError(
"Custom error",
PostizErrorCode.UNKNOWN_ERROR,
500,
undefined,
true
);
expect(isRecoverableError(error)).toBe(true);
});
});
});