Files
PriceUpdaterAppv2/tests/tui/errorHandling.test.js
2025-08-15 15:39:28 -05:00

180 lines
5.2 KiB
JavaScript

const {
enhanceError,
categorizeError,
isRetryableError,
createStandardError,
} = require("../../src/tui/utils/errorHandler");
const ErrorHandlingService = require("../../src/tui/services/ErrorHandlingService");
describe("TUI Error Handling", () => {
describe("Error Enhancement", () => {
test("should enhance basic error with troubleshooting", () => {
const originalError = new Error("Network timeout");
const enhanced = enhanceError(originalError, {
operation: "fetchTags",
screen: "TagAnalysisScreen",
});
expect(enhanced.troubleshooting).toBeDefined();
expect(enhanced.troubleshooting.length).toBeGreaterThan(0);
expect(enhanced.context.operation).toBe("fetchTags");
expect(enhanced.enhanced).toBe(true);
});
test("should not double-enhance errors", () => {
const originalError = new Error("Test error");
const enhanced1 = enhanceError(originalError);
const enhanced2 = enhanceError(enhanced1);
expect(enhanced2).toBe(enhanced1);
});
});
describe("Error Categorization", () => {
test("should categorize network errors correctly", () => {
const networkError = new Error("Connection timeout");
expect(categorizeError(networkError)).toBe("network");
const econnError = new Error("ECONNRESET");
econnError.code = "ECONNRESET";
expect(categorizeError(econnError)).toBe("network");
});
test("should categorize API errors correctly", () => {
const apiError = new Error("Shopify API rate limit exceeded");
expect(categorizeError(apiError)).toBe("api");
const httpError = new Error("HTTP 503 Service Unavailable");
httpError.code = "503";
expect(categorizeError(httpError)).toBe("api");
});
test("should categorize file errors correctly", () => {
const fileError = new Error("File not found");
fileError.code = "ENOENT";
expect(categorizeError(fileError)).toBe("file");
const permissionError = new Error("Permission denied");
permissionError.code = "EACCES";
expect(categorizeError(permissionError)).toBe("file");
});
test("should categorize validation errors correctly", () => {
const validationError = new Error("Invalid input format");
validationError.name = "ValidationError";
expect(categorizeError(validationError)).toBe("validation");
});
});
describe("Retry Logic", () => {
test("should identify retryable errors", () => {
const networkError = new Error("Connection timeout");
expect(isRetryableError(networkError)).toBe(true);
const rateLimitError = new Error("Rate limit exceeded");
expect(isRetryableError(rateLimitError)).toBe(true);
const validationError = new Error("Invalid input");
validationError.name = "ValidationError";
expect(isRetryableError(validationError)).toBe(false);
});
});
describe("Standard Errors", () => {
test("should create file not found error", () => {
const error = createStandardError("fileNotFound", {
file: "schedules.json",
});
expect(error.message).toContain("schedules.json");
expect(error.troubleshooting).toBeDefined();
expect(error.troubleshooting.length).toBeGreaterThan(0);
});
test("should create API connection error", () => {
const error = createStandardError("apiConnectionFailed");
expect(error.message).toContain("Shopify API");
expect(error.troubleshooting).toBeDefined();
});
});
describe("ErrorHandlingService", () => {
let errorHandler;
beforeEach(() => {
errorHandler = new ErrorHandlingService();
});
test("should execute operation successfully on first try", async () => {
const mockOperation = jest.fn().mockResolvedValue("success");
const result = await errorHandler.executeWithRetry(mockOperation);
expect(result).toBe("success");
expect(mockOperation).toHaveBeenCalledTimes(1);
});
test("should retry failed operations", async () => {
const mockOperation = jest
.fn()
.mockRejectedValueOnce(new Error("Network timeout"))
.mockResolvedValue("success");
const result = await errorHandler.executeWithRetry(mockOperation, {
maxRetries: 2,
});
expect(result).toBe("success");
expect(mockOperation).toHaveBeenCalledTimes(2);
});
test("should fail after max retries", async () => {
const mockOperation = jest
.fn()
.mockRejectedValue(new Error("Persistent error"));
await expect(
errorHandler.executeWithRetry(mockOperation, {
maxRetries: 1, // Only 1 retry, so should be called once
})
).rejects.toThrow();
expect(mockOperation).toHaveBeenCalledTimes(1);
});
test("should handle graceful file reads", async () => {
const mockFileReader = jest
.fn()
.mockRejectedValue(
Object.assign(new Error("File not found"), { code: "ENOENT" })
);
const result = await errorHandler.gracefulFileRead(
mockFileReader,
"fallback"
);
expect(result).toBe("fallback");
});
test("should track error history", async () => {
const mockOperation = jest
.fn()
.mockRejectedValue(new Error("Test error"));
try {
await errorHandler.executeWithRetry(mockOperation, { maxRetries: 1 });
} catch (error) {
// Expected to fail
}
const history = errorHandler.getErrorHistory();
expect(history.length).toBeGreaterThan(0);
const stats = errorHandler.getErrorStats();
expect(stats.totalErrors).toBeGreaterThan(0);
});
});
});