Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.5.0 - 2026-05-19
Added
wait_for_exit/2function — monitor-based replacement forProcess.sleep/1calls that wait for a process to die. Returns:okonce the target pid is no longer alive, or{:error, :timeout}after the configurable budget (default 1000ms). Handles already-dead pids correctly (monitor fires immediately with:noproc).LetItCrash.Asyncmodule — new public surface for testing async work.observe_async/1,2— wrap a block of test code and collect a%LetItCrash.Async.Report{}describing telemetry exception events that fired inside it (Task, Oban, LiveViewhandle_async/3). Handlers are isolated perobserve_asyncblock via$callerslineage tracking, so two concurrent observers running underasync: truedo not cross-pollute their reports.assert_no_silent_swallow/1,2— fail when a Task raised but nobody noticed.assert_all_completed/2— fail when async work did not finish within the supplied:withinwall-clock budget.assert_idempotent/2— fail when calling a 0-arity function twice produces different observable state. Takes a function (NOT a Report), because idempotency by definition requires a second execution. The user supplies a:state0-arity snapshot function.LetItCrash.Async.Reportstruct — pure data describing observed async work. Fields::spawned,:completed,:crashed,:exceptions,:warnings,:duration_ms,:started_at,:ended_at.
:telemetry ~> 1.0runtime dependency — required for the Async observer module. Loosened from a tighter pin so host apps on any1.xline of telemetry can adopt cleanly.- Dependabot config —
.github/dependabot.ymlopens weekly PRs for mix and GitHub Actions updates. - AGENTS.md alias style guide — one alias per line; no
alias Foo.{Bar, Baz}brace form.
Changed
start_tracking/0is now race-safe under concurrentasync: truetests. Previously a TOCTOU window between:ets.whereis/1and:ets.new/2could raiseArgumentError; that race is now rescued internally (benign — the table exists either way).use LetItCrashnow also importsLetItCrash.Async, so the new observer + assertions are available unqualified in test modules.- README leads with a Phoenix + Oban example showing
observe_asyncassert_no_silent_swallow+assert_all_completed+assert_idempotent. The supervisor-focused examples remain below.
Internal
- All seven
Process.sleep(10)calls in the test suite have been replaced withwait_for_exit/2. The test suite is now fully deterministic with respect to process termination.
Notes
assert_idempotent/2accepts afun, not aReport— this differs from an earlier draft because idempotency requires re-execution by definition. SeeLetItCrash.Async.assert_idempotent/2docs.- v0.5.0 does not capture
Loggeroutput, so Tasks that log errors but recover gracefully are still considered "completed normally". - Broadway / GenStage observer support is deferred to a future release.
- The
:sandboxoption onobserve_async/2is documentation-only in this release.
0.4.0 - 2026-01-19
Added
wait_for_process/2function - Waits for a registered process to exist and be alive- Useful in test setup when ensuring a process is available before interacting with it
- Configurable
:timeout(default: 1000ms) and:interval(default: 50ms) options - Returns
:okwhen process is found,{:error, :timeout}otherwise - Particularly helpful after starting supervisors or during async initialization
0.3.0 - 2025-10-21
Changed (Breaking)
- Refactored
crashAPI - Following Elixir best practices, removedcrash!/1function in favor ofcrash/2crash!/1has been removed (the!suffix is conventionally reserved for functions that raise exceptions)- New signature:
crash(process, type \\ :shutdown)wheretypecan be:shutdownor:kill - Follows the same convention as
Process.exit/2with the process as the first argument - Enables easy piping:
Process.whereis(:name) |> LetItCrash.crash(:kill) crash(pid)- default behavior (:shutdownsignal)crash(pid, :kill)- guarantees termination (cannot be trapped)- Maintains support for both PID and registered name with automatic PID tracking
- Tests updated to use the new API
Migration Guide
- Replace
crash!(process)withcrash(process, :kill) crash(process)continues to work the same way (uses:shutdownby default)- Argument order follows
Process.exit/2: process first, type second
0.2.0 - 2025-10-01
Added
crash!/1function - "Bang" version that uses:killsignal for guaranteed process termination- Works with processes that have
Process.flag(:trap_exit, true) - Cannot be trapped by the target process
- Particularly useful for testing GenServers with cleanup logic in
handle_info({:EXIT, ...}) - Supports both PID and registered name with automatic PID tracking
- Complete test suite including
TrapExitServerdemonstration
- Works with processes that have
Changed
- Updated module documentation to explain the difference between
crash/1andcrash!/1 - Enhanced README with "When to use
crash!/1?" section and practical examples - Added comparison table between
:shutdownand:killexit signals
Technical Details
:killexit signal bypasses process trapping mechanisms- Maintains same API signature as
crash/1for consistency - Includes integration tests with supervised trap_exit processes
0.1.0 - 2025-01-03
Core Functions
crash/1- Crashes processes by PID or registered name with automatic PID trackingrecovered?/1,2,3- Detects process recovery after crashes with multiple signatures- Automatic PID tracking when crashing by name
- Configurable timeout and interval options
- Manual PID comparison support
test_restart/2,3- Tests complete crash/recovery workflow by running functions before and after
Advanced Testing Functions
assert_clean_registry/2,3- Verifies Registry entries are properly cleaned up on crash and recreated on recoveryverify_ets_cleanup/2,3- Monitors ETS table entries for proper cleanup during process crashes- Support for
expect_cleanupandexpect_recreateoptions - Configurable timeout for verification
- Detects resource leaks and improper state management
- Support for
Development Infrastructure
- Code Quality: Credo static code analysis integration with strict mode (0 issues)
- CI/CD Pipeline: GitHub Actions with comprehensive testing
- Tests on Elixir 1.17.2 + OTP 26.0
- Automated formatting, compilation warnings, and Credo checks
- 15 tests covering all functionality, 0 failures
- Documentation: ExDoc integration with HTML output
- Complete API documentation with practical examples
- Advanced usage examples for Registry and ETS testing
- README and CHANGELOG integration
Technical Features
- ✅ Safe process crashes (automatic unlink to prevent test failures)
- ✅ Real recovery detection via PID comparison
- ✅ Supervised process support (GenServers, Agents, custom processes)
- ✅ Resource cleanup validation (Registry entries, ETS tables)
- ✅ Simple and intuitive API with comprehensive error handling
- ✅ Zero external runtime dependencies
- ✅ Automatic tracking system using ETS for PID management
Project Setup
- MIT License with complete contribution guidelines
- Project badges for CI status, license, and Elixir compatibility
- Issue and PR templates for community contributions
- Comprehensive test coverage with realistic usage examples