Start on step 17 next
This commit is contained in:
179
tests/tui/errorHandling.test.js
Normal file
179
tests/tui/errorHandling.test.js
Normal file
@@ -0,0 +1,179 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user