Files
automaker/libs/utils/tests/error-handler.test.ts
Kacper 0cef537a3d test: Add comprehensive unit tests for shared packages
Add 88 new unit tests covering critical business logic in shared packages:

- libs/git-utils/tests/diff.test.ts (22 tests)
  * Synthetic diff generation for new files
  * Binary file handling
  * Large file handling
  * Untracked file diff appending
  * Directory file listing with exclusions
  * Non-git directory handling

- libs/dependency-resolver/tests/resolver.test.ts (30 tests)
  * Topological sorting with dependencies
  * Priority-aware ordering
  * Circular dependency detection
  * Missing dependency tracking
  * Blocked feature detection
  * Complex dependency graphs

- libs/utils/tests/error-handler.test.ts (36 tests)
  * Abort error detection
  * Cancellation error detection
  * Authentication error detection
  * Error classification logic
  * User-friendly error messages

All tests use vitest and follow best practices with proper setup/teardown.

Resolves PR review issue #1 (HIGH PRIORITY)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-20 22:48:43 +01:00

262 lines
9.1 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
isAbortError,
isCancellationError,
isAuthenticationError,
classifyError,
getUserFriendlyErrorMessage,
} from "../src/error-handler";
describe("error-handler.ts", () => {
describe("isAbortError", () => {
it("should return true for Error with name 'AbortError'", () => {
const error = new Error("Operation aborted");
error.name = "AbortError";
expect(isAbortError(error)).toBe(true);
});
it("should return true for Error with message containing 'abort'", () => {
const error = new Error("Request was aborted");
expect(isAbortError(error)).toBe(true);
});
it("should return false for regular Error", () => {
const error = new Error("Something went wrong");
expect(isAbortError(error)).toBe(false);
});
it("should return false for non-Error values", () => {
expect(isAbortError("abort")).toBe(false);
expect(isAbortError(null)).toBe(false);
expect(isAbortError(undefined)).toBe(false);
expect(isAbortError({})).toBe(false);
});
it("should handle Error with both AbortError name and abort message", () => {
const error = new Error("abort");
error.name = "AbortError";
expect(isAbortError(error)).toBe(true);
});
});
describe("isCancellationError", () => {
it("should return true for 'cancelled' message", () => {
expect(isCancellationError("Operation cancelled")).toBe(true);
expect(isCancellationError("CANCELLED")).toBe(true);
});
it("should return true for 'canceled' message (US spelling)", () => {
expect(isCancellationError("Operation canceled")).toBe(true);
expect(isCancellationError("CANCELED")).toBe(true);
});
it("should return true for 'stopped' message", () => {
expect(isCancellationError("Process stopped")).toBe(true);
expect(isCancellationError("STOPPED")).toBe(true);
});
it("should return true for 'aborted' message", () => {
expect(isCancellationError("Request aborted")).toBe(true);
expect(isCancellationError("ABORTED")).toBe(true);
});
it("should return false for non-cancellation messages", () => {
expect(isCancellationError("Something went wrong")).toBe(false);
expect(isCancellationError("Error occurred")).toBe(false);
expect(isCancellationError("")).toBe(false);
});
it("should be case-insensitive", () => {
expect(isCancellationError("CaNcElLeD")).toBe(true);
expect(isCancellationError("StOpPeD")).toBe(true);
});
});
describe("isAuthenticationError", () => {
it("should return true for 'Authentication failed' message", () => {
expect(isAuthenticationError("Authentication failed")).toBe(true);
});
it("should return true for 'Invalid API key' message", () => {
expect(isAuthenticationError("Invalid API key provided")).toBe(true);
});
it("should return true for 'authentication_failed' message", () => {
expect(isAuthenticationError("Error: authentication_failed")).toBe(true);
});
it("should return true for 'Fix external API key' message", () => {
expect(isAuthenticationError("Fix external API key configuration")).toBe(true);
});
it("should return false for non-authentication errors", () => {
expect(isAuthenticationError("Something went wrong")).toBe(false);
expect(isAuthenticationError("Network error")).toBe(false);
expect(isAuthenticationError("")).toBe(false);
});
it("should be case-sensitive", () => {
expect(isAuthenticationError("authentication failed")).toBe(false);
expect(isAuthenticationError("AUTHENTICATION FAILED")).toBe(false);
});
});
describe("classifyError", () => {
it("should classify authentication errors", () => {
const error = new Error("Authentication failed");
const result = classifyError(error);
expect(result.type).toBe("authentication");
expect(result.isAuth).toBe(true);
expect(result.isAbort).toBe(false);
expect(result.isCancellation).toBe(false);
expect(result.message).toBe("Authentication failed");
expect(result.originalError).toBe(error);
});
it("should classify abort errors", () => {
const error = new Error("aborted");
const result = classifyError(error);
expect(result.type).toBe("abort");
expect(result.isAbort).toBe(true);
expect(result.isAuth).toBe(false);
expect(result.message).toBe("aborted");
});
it("should classify AbortError by name", () => {
const error = new Error("Request cancelled");
error.name = "AbortError";
const result = classifyError(error);
expect(result.type).toBe("abort");
expect(result.isAbort).toBe(true);
});
it("should classify cancellation errors", () => {
const error = new Error("Operation cancelled");
const result = classifyError(error);
expect(result.type).toBe("cancellation");
expect(result.isCancellation).toBe(true);
expect(result.isAbort).toBe(false);
});
it("should classify execution errors (regular Error)", () => {
const error = new Error("Something went wrong");
const result = classifyError(error);
expect(result.type).toBe("execution");
expect(result.isAuth).toBe(false);
expect(result.isAbort).toBe(false);
expect(result.isCancellation).toBe(false);
});
it("should classify unknown errors (non-Error)", () => {
const result = classifyError("string error");
expect(result.type).toBe("unknown");
expect(result.message).toBe("string error");
});
it("should handle null/undefined errors", () => {
const result1 = classifyError(null);
expect(result1.type).toBe("unknown");
expect(result1.message).toBe("Unknown error");
const result2 = classifyError(undefined);
expect(result2.type).toBe("unknown");
expect(result2.message).toBe("Unknown error");
});
it("should prioritize authentication over abort", () => {
const error = new Error("Authentication failed - aborted");
const result = classifyError(error);
expect(result.type).toBe("authentication");
expect(result.isAuth).toBe(true);
expect(result.isAbort).toBe(true); // Both flags can be true
});
it("should prioritize abort over cancellation", () => {
const error = new Error("Request cancelled");
error.name = "AbortError";
const result = classifyError(error);
expect(result.type).toBe("abort");
expect(result.isAbort).toBe(true);
expect(result.isCancellation).toBe(true); // Both flags can be true
});
it("should convert object errors to string", () => {
const result = classifyError({ code: 500, message: "Server error" });
expect(result.message).toContain("Object");
});
it("should convert number errors to string", () => {
const result = classifyError(404);
expect(result.message).toBe("404");
expect(result.type).toBe("unknown");
});
});
describe("getUserFriendlyErrorMessage", () => {
it("should return friendly message for abort errors", () => {
const error = new Error("abort");
const message = getUserFriendlyErrorMessage(error);
expect(message).toBe("Operation was cancelled");
});
it("should return friendly message for AbortError by name", () => {
const error = new Error("Something");
error.name = "AbortError";
const message = getUserFriendlyErrorMessage(error);
expect(message).toBe("Operation was cancelled");
});
it("should return friendly message for authentication errors", () => {
const error = new Error("Authentication failed");
const message = getUserFriendlyErrorMessage(error);
expect(message).toBe("Authentication failed. Please check your API key.");
});
it("should prioritize abort message over auth", () => {
const error = new Error("Authentication failed - abort");
const message = getUserFriendlyErrorMessage(error);
// Auth is checked first in classifyError, but abort check happens before auth in getUserFriendlyErrorMessage
expect(message).toBe("Operation was cancelled");
});
it("should return original message for other errors", () => {
const error = new Error("Network timeout");
const message = getUserFriendlyErrorMessage(error);
expect(message).toBe("Network timeout");
});
it("should handle non-Error values", () => {
expect(getUserFriendlyErrorMessage("string error")).toBe("string error");
expect(getUserFriendlyErrorMessage(null)).toBe("Unknown error");
expect(getUserFriendlyErrorMessage(undefined)).toBe("Unknown error");
});
it("should return original message for cancellation errors", () => {
const error = new Error("Operation cancelled by user");
const message = getUserFriendlyErrorMessage(error);
expect(message).toBe("Operation cancelled by user");
});
it("should handle Error without message", () => {
const error = new Error();
const message = getUserFriendlyErrorMessage(error);
expect(message).toBe("");
});
});
});