CHANGELOG.md

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.4.0] - 2025-10-13

### Added
- **Automatic Redaction System** - ExVCR-style `redact_cassette` macro for programmatic data redaction
  - **`redact_cassette` Macro** - Wrap API calls to automatically redact sensitive data during recording and replay
  - **User-Defined Redaction Functions** - Custom functions for `response_body_json`, `response_body_raw`, `request_headers`, `response_headers`, and `url` redaction
  - **JSON-Aware Redaction** - Automatic JSON detection and encoding/decoding using configurable `Reqord.JSON` behavior
  - **Named Redactors** - Define reusable redaction configurations in application config and reference by name
  - **Flexible Function Support** - Inline functions, named functions, module/function tuples, and config-based redactors
  - **Bidirectional Application** - Redaction applied during both recording (to cassettes) and replay (to test responses) for consistency
  - **Content-Type Detection** - Smart detection of JSON vs binary content for appropriate redaction handling
- **Object and Streaming Support** - Comprehensive support for binary data and streaming responses
  - **Smart Content Detection** - Automatic detection of binary vs text content based on Content-Type headers and heuristics
  - **External Object Storage** - Large binary objects stored externally to prevent JSONL bloat
  - **Streaming Response Support** - Capture and replay of streaming responses (SSE, chunked transfer)
  - **Configurable Storage Thresholds** - Control when to use external vs inline storage
  - **Enhanced CassetteEntry.Response** - New fields for body_encoding, body_external_ref, and stream_metadata
  - **Pluggable Storage Extensions** - Extended Storage.Behavior with object and stream storage methods
- **Flexible Cassette Organization** - Multiple strategies for organizing cassettes
  - **Explicit Path Override** - Use `:vcr_path` tag to specify exact cassette path per test
  - **Named Path Builders** - Define reusable path builders in config and reference by name in test modules
  - **Global Path Builder** - Configure a single path builder function for all tests
  - **Macro Context Support** - New `Reqord.Case.set_cassette_context/1` for passing compile-time variables to path builders
  - **Priority System** - Clear precedence: vcr_path > named builder > global builder > vcr tag > default
  - **Documentation** - Comprehensive guides in `docs/CASSETTE_ORGANIZATION.md` and `docs/MACRO_SUPPORT.md`
- **Manual Cassette Editing** - New `mix reqord.edit` task for redacting sensitive data
  - Opens cassettes in your editor with automatic JSONL parsing and formatting
  - Validates JSON structure after editing before saving
  - Filter by entry index or URL pattern
  - Useful for redacting emails, account IDs, and other PII from response bodies
- **Improved Mix Task Path Handling** - All Mix tasks now support flexible path resolution
  - Accept short names (relative to cassette dir), relative paths, and absolute paths
  - Consistent with `mix test` command usage
  - Shared `Reqord.Tasks.Helpers` module reduces code duplication across tasks

### Changed
- **Enhanced Manual Redaction** - Improved `mix reqord.edit` task to work seamlessly with automatic redaction system
- **Enhanced Response Creation** - New `CassetteEntry.Response.new_with_raw_body/3` for automatic encoding detection
- **Improved Replay System** - Enhanced body loading with support for external storage and streaming content
- **Extended Configuration** - New config options for max_inline_size, object_directory, binary_storage, and stream_speed
- **Documentation Organization** - Moved guide documents to `docs/` directory and added to hex docs
- **README Simplification** - Streamlined README to focus on quick start and core features
  - Split detailed content into focused guides (Getting Started, Security, Advanced Configuration)
  - Reduced from 691 to ~250 lines for better new-user experience
  - Added clear navigation to comprehensive documentation
- **New Documentation Guides**
  - **Getting Started Guide** - Simple, beginner-friendly introduction with security basics
  - **Security Guide** - Comprehensive redaction documentation with best practices, pre-commit hooks, and emergency procedures
  - **Advanced Configuration Guide** - Detailed feature documentation for request matching, binary data, streaming, and performance tuning

## [0.3.0] - 2025-10-05

### Added
- **NEW ARCHITECTURE: Timestamp-based chronological ordering** - Complete redesign to solve request ordering issues
  - **Microsecond precision timestamps** - All cassette entries now include `recorded_at` field with microsecond timestamps
  - **Async CassetteWriter GenServer** - Non-blocking writes with batching and automatic timestamp sorting
  - **Streaming CassetteReader** - Memory-efficient reading with chronological ordering by timestamp
  - **Pluggable Storage Backend** - New `Reqord.Storage.Behavior` interface for future S3/Redis support
  - **FileSystem Storage Backend** - Optimized JSONL file operations with atomic writes and streaming reads
  - **Application Supervision** - CassetteWriter automatically starts with Reqord application
  - **Enhanced Test Flushing** - Automatic cassette flushing on test completion via `Reqord.cleanup/1`
- **Pure Sequential Streaming** - Revolutionary performance improvement for cassette replay
  - **No search operations** - Direct O(1) access instead of O(n) searching through entries
  - **Sequential verification** - Takes next entry and verifies match instead of searching for matches
  - **Optimized common case** - Fast path for default `[:method, :uri]` matching used in 90%+ of cases

### Fixed
- **CRITICAL: POST-DELETE lifecycle ordering** - Solved concurrent request recording order issues
  - **Problem**: Concurrent requests (e.g., parallel POST-DELETE lifecycles) were recorded in completion order, not initiation order
  - **Solution**: Timestamp-based recording ensures chronological replay even when requests complete out of order
  - **Impact**: Eliminates ID mismatch errors in cassette replay for concurrent scenarios
  - **Example**: POST (user creation) → DELETE (user deletion) lifecycles now maintain correct order during replay
- **CRITICAL: Concurrent request recording in :all mode** - Fixed issue where HTTP requests made from spawned processes weren't being recorded
  - Problem: Reqord used process dictionary to accumulate requests in `:all` mode, but spawned processes (Task.async, etc.) don't inherit parent's process dictionary
  - Solution: Replaced process dictionary with GenServer-based state management following ExVCR's proven architecture pattern
  - Impact: Concurrent HTTP requests from Task.async and other spawned processes are now properly recorded in `:all` mode
  - Scope: Only affects `:all` mode - other modes (`:once`, `:new_episodes`, `:none`) unchanged and working correctly

### Changed
- **BREAKING: Cassette format change** - All cassettes now require timestamps
  - **Migration Required**: Existing cassettes without timestamps will not load
  - **Solution**: Regenerate all cassettes using `REQORD=all mix test`
  - **Benefit**: Enables chronological replay ordering and future extensibility
- **BREAKING: Removed legacy timestamp compatibility** - Clean implementation without backward compatibility
  - No automatic timestamp addition for legacy entries
  - Simplified codebase with consistent timestamp requirements
  - Better error messages for invalid cassette entries
- **BREAKING: Sequential replay is now the only strategy** - Removed complex dual matching approaches
  - **Eliminated**: Search-based "last match wins" strategy that was slower and more error-prone
  - **Unified**: Single sequential streaming approach for all scenarios
  - **Improved**: Better error messages with `SequenceMismatchError` showing exact position and expected vs actual requests
  - **Simplified**: Much cleaner codebase with reduced cognitive overhead

### Removed
- **Legacy cassette support** - No longer supports cassettes without timestamps for cleaner architecture
- **Search-based matching** - Removed complex search algorithms in favor of simple sequential access
- **Duplicate code** - Eliminated duplicate `put_headers` functions across modules

### Performance
- **MAJOR: O(1) cassette replay** - Eliminated O(n) search operations for massive performance improvement on large cassettes
- **Async writes** - Non-blocking cassette writes during test execution
- **Streaming operations** - Memory-efficient reading for large cassette files
- **Batched I/O** - Reduced file system operations through intelligent batching
- **Timestamp sorting** - Automatic chronological ordering without manual intervention
- **Optimized matching** - Inlined fast path for most common matching scenarios

## [0.2.2] - 2025-10-03

### Fixed
- **CRITICAL: Multiple requests in :all mode** - Fixed `:all` mode to properly handle multiple requests
  - In `:all` mode, each request now accumulates and replaces the entire test cassette with all requests
  - This ensures all requests in a test are recorded while never clearing cassettes from other tests
  - Each test gets a fresh cassette containing only its requests when using `REQORD=all`
  - Fixes the issue where running `REQORD=all mix test specific_test.exs` would inappropriately clear cassettes

## [0.2.1] - 2025-10-03

### Fixed
- **CRITICAL: Record mode behavior** - Fixed `:all` mode to follow Ruby VCR behavior
  - `:all` mode now always replaces the entire cassette (instead of appending)
  - This fixes the critical issue where rerecording would mix old broken requests with new fixed ones
  - Simplified implementation by removing complex session tracking in favor of Ruby VCR's straightforward approach
  - `:all` mode now never replays - always goes live and records fresh responses
  - Ensures that `REQORD=all` provides clean, predictable cassette replacement behavior
- **Code quality** - Fixed all test warnings and Credo issues
  - Resolved unused variable warnings in test files
  - Fixed alias ordering and unused alias warnings
  - Refactored complex functions to reduce cyclomatic complexity
  - Improved code readability and maintainability

### Added
- **Comprehensive test coverage** - Extensive edge case testing across all modules
  - Redactor tests for extreme token lengths (1-10000 characters) and boundary cases
  - URL normalization edge cases including malformed URLs and unicode handling
  - Base64 encoding/decoding stress tests with large data and concurrent operations
  - Cassette file format edge cases (mixed line endings, BOM markers, malformed JSON)
  - HTTP request/response validation for unusual methods and status codes
  - Record mode integration tests simulating real-world broken→fixed workflows
  - Last-match-wins replay strategy tests for handling mixed cassette scenarios
  - Large file handling and concurrent access patterns
  - Memory and performance considerations for encoding operations
- **Documentation improvements** - Added hex package badges to README

### Changed
- **Simplified architecture** - Removed Session module complexity
  - Eliminated Agent-based session tracking that was causing confusion
  - Record logic now follows simple Ruby VCR patterns without stateful tracking
  - Cleaner, more predictable behavior that matches developer expectations
  - Reduced cognitive overhead for understanding and debugging record modes

## [0.2.0] - 2025-10-02

### Added
- **Pluggable JSON system** - Support for custom JSON libraries via `Reqord.JSON` behavior
  - `Reqord.JSON.Jason` adapter (default) with runtime availability checks
  - `Reqord.JSON.Poison` adapter with graceful fallback when not available
  - Application config: `config :reqord, :json_library, MyAdapter`
  - Comprehensive adapter test suite with real cassette creation/loading
- **CassetteEntry struct** - Type-safe cassette data modeling with validation
  - Nested `Reqord.CassetteEntry.Request` and `Response` structs
  - Built-in validation with helpful error messages
  - Helper functions: `new/2`, `from_raw/1`, `to_map/1`, `validate/1`
- **Test utilities** - Shared helpers to reduce test code duplication
  - `Reqord.TestHelpers.with_module/3` for conditional module tests
  - `Reqord.TestHelpers.with_config/4` for application config setup/teardown
  - `Reqord.TestHelpers.with_module_and_config/6` combining both patterns
- **Development tools** - Mix task for code quality enforcement
  - `mix precommit` alias running format, credo, dialyzer, and tests
- **Configurable settings** - Made hard-coded values configurable for flexibility
  - `Reqord.Config` module for centralized configuration management
  - Configurable cassette directory: `config :reqord, :cassette_dir, "custom/path"`
  - Configurable auth parameters: `config :reqord, :auth_params, ~w[token my_token]`
  - Configurable auth headers: `config :reqord, :auth_headers, ~w[authorization x-my-auth]`
  - Configurable volatile headers: `config :reqord, :volatile_headers, ~w[date x-trace-id]`
  - Configuration validation with helpful error messages
  - Comprehensive test coverage for all configuration options

### Changed
- **Improved error handling** throughout the codebase
  - Replaced bare `rescue _ -> body` clauses with specific exception handling
  - Added logging for JSON decoding failures and network errors during recording
  - Consistent use of `case` statements instead of exception-prone `!` functions
- **Better test organization** - Separated JSONL format tests into dedicated file
  - `test/reqord/jsonl_test.exs` for JSONL-specific functionality
  - `test/reqord/json/` directory for JSON adapter tests
  - Improved test cleanup preserving fixture files
- **Optional dependencies** - Made JSON libraries optional to reduce bloat
  - Jason and Poison marked as `optional: true` in mix.exs
  - Runtime checks with helpful error messages when libraries are missing

### Fixed
- **Error handling security** - Prevented silent failures that could mask issues
- **Test reliability** - Fixed cassette cleanup logic to preserve fixture directories
- **Code quality** - Resolved Credo warnings for negated conditions and formatting
- **Hard-coded values** - Eliminated hard-coded cassette directory, auth parameters, and volatile headers
  - All previously hard-coded values are now configurable via application config
  - Maintains backward compatibility with sensible defaults

## [0.1.0] - 2025-10-02

### Added
- Initial release of Reqord
- Core `Reqord` module with `install!/1` for setting up VCR stubs
- Ruby VCR-style record modes: `:once`, `:new_episodes`, `:all`, `:none`
  - `:once` - Strict replay, raise on new requests (default)
  - `:new_episodes` - Replay existing, record new requests (append mode)
  - `:all` - Always re-record everything, ignore existing cassette
  - `:none` - Never record, never hit network (must have complete cassette)
- `Reqord.Case` ExUnit case template for automatic cassette management
- Smart request matching based on method, normalized URL, and body hash
- Automatic redaction of sensitive headers (`authorization`)
- Automatic redaction of auth query parameters (`token`, `apikey`, `api_key`)
- Automatic removal of volatile response headers (`date`, `server`, `set-cookie`, etc.)
- JSONL cassette format stored in `test/support/cassettes/`
- Query parameter normalization (lexicographic sorting, auth param removal)
- Body hash differentiation for POST/PUT/PATCH requests
- Support for spawned processes via `allow/3` helper
- Multiple configuration options for record mode:
  - Environment variable via `REQORD`
  - Application config via `:reqord, :default_mode`
  - Per-test override via `@tag vcr_mode: :mode`
- Automatic cassette naming based on test module and test name
- Custom cassette naming via `@tag vcr: "custom_name"`
- Custom stub name override via `@tag req_stub_name: MyStub`
- Integration with `Req.Test` for zero application code changes
- Comprehensive test suite covering all modes and features
- Detailed README with setup, usage examples, and troubleshooting
- Mix tasks for cassette management:
  - `mix reqord.show` - Display cassette contents with filtering options
  - `mix reqord.audit` - Audit cassettes for secrets, staleness, and unused entries
  - `mix reqord.prune` - Clean up empty files, duplicates, and stale cassettes
  - `mix reqord.rename` - Rename or move cassettes, with migration support
- Flexible request matching system:
  - Built-in matchers: `:method`, `:uri`, `:host`, `:path`, `:headers`, `:body`
  - Custom matcher registration via `register_matcher/2`
  - Default matching on `[:method, :uri]`
  - Per-test matcher override via `@tag match_on: [...matchers]`
  - Application config for default matchers
- Test API application (`test_api/`) for demonstrating Reqord:
  - Simple REST API with authentication
  - Multiple routes (GET /api/users, GET /api/users/:id, POST /api/users)
  - Fake Bearer token authentication
  - Example tests showing real-world usage
- Automated cassette recording script (`scripts/record_cassettes.sh`):
  - Automatically starts test API server
  - Records all example cassettes
  - Stops server when complete
- Comprehensive secret redaction system (`Reqord.Redactor`):
  - **CRITICAL SECURITY**: Ensures secrets never get committed to git cassettes
  - Built-in redaction for auth headers, query parameters, response bodies
  - VCR-style configurable filters for app-specific secrets
  - Multi-layer protection: headers → query params → JSON keys → pattern matching
  - Automatic redaction of Bearer tokens, API keys, long alphanumeric strings
  - Support for GitHub tokens, Stripe keys, UUIDs, and custom patterns

[Unreleased]: https://github.com/Makesesama/reqord/compare/v0.4.0...HEAD
[0.4.0]: https://github.com/Makesesama/reqord/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/Makesesama/reqord/compare/v0.2.2...v0.3.0
[0.2.2]: https://github.com/Makesesama/reqord/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/Makesesama/reqord/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/Makesesama/reqord/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/Makesesama/reqord/releases/tag/v0.1.0