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); }); }); });