mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
242 lines
8.1 KiB
Python
242 lines
8.1 KiB
Python
"""Unit tests for domain errors and exit codes.
|
|
|
|
Covers all 13 error types with their exit code mappings.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
_scripts_dir = Path(__file__).resolve().parents[2] / "scripts"
|
|
if str(_scripts_dir) not in sys.path:
|
|
sys.path.insert(0, str(_scripts_dir))
|
|
|
|
from binary_analysis.domain.enums import ExitCode
|
|
from binary_analysis.domain.errors import (
|
|
AmbiguousSelectorError,
|
|
AnalysisFailedError,
|
|
BackendFailureError,
|
|
BinaryAnalysisError,
|
|
BinaryNotFoundError,
|
|
DependencyMissingError,
|
|
EntityNotFoundError,
|
|
ImportFailedError,
|
|
InvalidArgsError,
|
|
InvalidConfigError,
|
|
OperationTimeoutError,
|
|
ProjectNotFoundError,
|
|
UnsupportedFormatError,
|
|
error_type_for,
|
|
)
|
|
|
|
|
|
class TestExitCodes:
|
|
"""Tests for exit code enumeration."""
|
|
|
|
def test_success_is_0(self) -> None:
|
|
assert ExitCode.SUCCESS == 0
|
|
|
|
def test_invalid_args_is_2(self) -> None:
|
|
assert ExitCode.INVALID_ARGS == 2
|
|
|
|
def test_dependency_missing_is_3(self) -> None:
|
|
assert ExitCode.DEPENDENCY_MISSING == 3
|
|
|
|
def test_all_codes_are_unique(self) -> None:
|
|
values = [e.value for e in ExitCode]
|
|
assert len(values) == len(set(values))
|
|
|
|
def test_all_14_exit_codes_defined(self) -> None:
|
|
assert len(ExitCode) == 14
|
|
|
|
|
|
class TestBinaryAnalysisError:
|
|
"""Tests for BinaryAnalysisError base class."""
|
|
|
|
def test_default_exit_code(self) -> None:
|
|
error = BinaryAnalysisError("test error")
|
|
assert error.exit_code == ExitCode.GENERIC_ERROR
|
|
assert error.message == "test error"
|
|
|
|
def test_custom_exit_code(self) -> None:
|
|
error = BinaryAnalysisError("test error", ExitCode.BACKEND_FAILURE)
|
|
assert error.exit_code == ExitCode.BACKEND_FAILURE
|
|
|
|
def test_to_diagnostic(self) -> None:
|
|
error = BinaryAnalysisError("something went wrong")
|
|
diag = error.to_diagnostic()
|
|
assert diag["severity"] == "ERROR"
|
|
assert diag["message"] == "something went wrong"
|
|
|
|
def test_is_exception(self) -> None:
|
|
error = BinaryAnalysisError("test")
|
|
assert isinstance(error, Exception)
|
|
|
|
|
|
class TestInvalidArgsError:
|
|
"""Tests for InvalidArgsError."""
|
|
|
|
def test_exit_code_is_2(self) -> None:
|
|
error = InvalidArgsError("bad args")
|
|
assert error.exit_code == ExitCode.INVALID_ARGS
|
|
|
|
def test_message_preserved(self) -> None:
|
|
error = InvalidArgsError("limit must be a positive integer")
|
|
assert "limit must be a positive integer" in error.message
|
|
|
|
|
|
class TestDependencyMissingError:
|
|
"""Tests for DependencyMissingError."""
|
|
|
|
def test_exit_code_is_3(self) -> None:
|
|
error = DependencyMissingError("Ghidra not found")
|
|
assert error.exit_code == ExitCode.DEPENDENCY_MISSING
|
|
|
|
def test_message_preserved(self) -> None:
|
|
error = DependencyMissingError("Java not installed")
|
|
assert "Java not installed" in error.message
|
|
|
|
|
|
class TestInvalidConfigError:
|
|
"""Tests for InvalidConfigError."""
|
|
|
|
def test_exit_code_is_4(self) -> None:
|
|
error = InvalidConfigError("corrupt project.json")
|
|
assert error.exit_code == ExitCode.INVALID_CONFIG
|
|
|
|
def test_to_diagnostic(self) -> None:
|
|
error = InvalidConfigError("corrupt project.json")
|
|
diag = error.to_diagnostic()
|
|
assert diag["severity"] == "ERROR"
|
|
assert diag["category"] == "config"
|
|
|
|
|
|
class TestUnsupportedFormatError:
|
|
"""Tests for UnsupportedFormatError."""
|
|
|
|
def test_exit_code_is_5(self) -> None:
|
|
error = UnsupportedFormatError("unknown format")
|
|
assert error.exit_code == ExitCode.UNSUPPORTED_FORMAT
|
|
|
|
|
|
class TestProjectNotFoundError:
|
|
"""Tests for ProjectNotFoundError."""
|
|
|
|
def test_exit_code_is_6(self) -> None:
|
|
error = ProjectNotFoundError("my-project")
|
|
assert error.exit_code == ExitCode.PROJECT_NOT_FOUND
|
|
assert "my-project" in error.message
|
|
|
|
|
|
class TestBinaryNotFoundError:
|
|
"""Tests for BinaryNotFoundError."""
|
|
|
|
def test_exit_code_is_7(self) -> None:
|
|
error = BinaryNotFoundError()
|
|
assert error.exit_code == ExitCode.BINARY_NOT_FOUND
|
|
assert "binary" in error.message.lower()
|
|
|
|
|
|
class TestAmbiguousSelectorError:
|
|
"""Tests for AmbiguousSelectorError."""
|
|
|
|
def test_exit_code_is_8(self) -> None:
|
|
error = AmbiguousSelectorError("ambiguous", [])
|
|
assert error.exit_code == ExitCode.AMBIGUOUS_SELECTOR
|
|
|
|
def test_to_diagnostic_with_candidates(self) -> None:
|
|
candidates = [{"name": "func1"}, {"name": "func2"}]
|
|
error = AmbiguousSelectorError("ambiguous selector", candidates)
|
|
diag = error.to_diagnostic()
|
|
assert "candidates" in diag
|
|
assert len(diag["candidates"]) == 2
|
|
|
|
|
|
class TestEntityNotFoundError:
|
|
"""Tests for EntityNotFoundError."""
|
|
|
|
def test_exit_code_is_9(self) -> None:
|
|
error = EntityNotFoundError("Function", "my_func")
|
|
assert error.exit_code == ExitCode.ENTITY_NOT_FOUND
|
|
assert error.entity_type == "Function"
|
|
assert error.selector == "my_func"
|
|
|
|
def test_message_contains_type_and_selector(self) -> None:
|
|
error = EntityNotFoundError("Function", "nonexistent")
|
|
assert "Function" in error.message
|
|
assert "nonexistent" in error.message
|
|
|
|
|
|
class TestImportFailedError:
|
|
"""Tests for ImportFailedError."""
|
|
|
|
def test_exit_code_is_10(self) -> None:
|
|
error = ImportFailedError("disk full")
|
|
assert error.exit_code == ExitCode.IMPORT_FAILED
|
|
|
|
def test_binary_path_preserved(self) -> None:
|
|
error = ImportFailedError("failed", binary_path="/tmp/test.bin")
|
|
assert error.binary_path == "/tmp/test.bin"
|
|
|
|
|
|
class TestAnalysisFailedError:
|
|
"""Tests for AnalysisFailedError."""
|
|
|
|
def test_exit_code_is_11(self) -> None:
|
|
error = AnalysisFailedError("analysis crashed")
|
|
assert error.exit_code == ExitCode.ANALYSIS_FAILED
|
|
|
|
def test_project_preserved(self) -> None:
|
|
error = AnalysisFailedError("analysis crashed", project="my-proj")
|
|
assert error.project == "my-proj"
|
|
|
|
|
|
class TestOperationTimeoutError:
|
|
"""Tests for OperationTimeoutError."""
|
|
|
|
def test_exit_code_is_12(self) -> None:
|
|
error = OperationTimeoutError()
|
|
assert error.exit_code == ExitCode.OPERATION_TIMEOUT
|
|
|
|
def test_to_diagnostic(self) -> None:
|
|
error = OperationTimeoutError("timed out after 5s")
|
|
diag = error.to_diagnostic()
|
|
assert diag["severity"] == "ERROR"
|
|
assert diag["category"] == "timeout"
|
|
assert diag["recoverable"] is True
|
|
|
|
|
|
class TestBackendFailureError:
|
|
"""Tests for BackendFailureError."""
|
|
|
|
def test_exit_code_is_13(self) -> None:
|
|
error = BackendFailureError("backend internal error")
|
|
assert error.exit_code == ExitCode.BACKEND_FAILURE
|
|
|
|
def test_to_diagnostic_with_original_error(self) -> None:
|
|
error = BackendFailureError("backend failed", original_error="NullPointerException")
|
|
diag = error.to_diagnostic()
|
|
assert diag["backend_error"] == "NullPointerException"
|
|
|
|
|
|
class TestErrorTypeFor:
|
|
"""Tests for exit code to error type mapping."""
|
|
|
|
def test_all_codes_mapped(self) -> None:
|
|
for code in ExitCode:
|
|
error_cls = error_type_for(code)
|
|
assert issubclass(error_cls, BinaryAnalysisError)
|
|
|
|
def test_specific_mappings(self) -> None:
|
|
assert error_type_for(ExitCode.INVALID_ARGS) is InvalidArgsError
|
|
assert error_type_for(ExitCode.INVALID_CONFIG) is InvalidConfigError
|
|
assert error_type_for(ExitCode.UNSUPPORTED_FORMAT) is UnsupportedFormatError
|
|
assert error_type_for(ExitCode.PROJECT_NOT_FOUND) is ProjectNotFoundError
|
|
assert error_type_for(ExitCode.BINARY_NOT_FOUND) is BinaryNotFoundError
|
|
assert error_type_for(ExitCode.AMBIGUOUS_SELECTOR) is AmbiguousSelectorError
|
|
assert error_type_for(ExitCode.ENTITY_NOT_FOUND) is EntityNotFoundError
|
|
assert error_type_for(ExitCode.IMPORT_FAILED) is ImportFailedError
|
|
assert error_type_for(ExitCode.ANALYSIS_FAILED) is AnalysisFailedError
|
|
assert error_type_for(ExitCode.OPERATION_TIMEOUT) is OperationTimeoutError
|
|
assert error_type_for(ExitCode.BACKEND_FAILURE) is BackendFailureError
|